天天看点

Profiling (移动设备性能分析)官方文档笔记Memory内存

本文档主要是对Unity官方教程的个人理解与总结(其实以翻译记录为主:>)

仅作为个人学习使用,不得作为商业用途,欢迎转载,并请注明出处。

文章中涉及到的操作都是基于 Unity2017.3版本

参考链接: https://docs.unity3d.com/Manual/MobileProfiling.html

Profiling

First steps

(未完待续)

Memory

内存

There are two types of memory, Mono memory and Unity memory.

有两种类型的内存:Mono 内存和Unity内存。

Mono memory

Mono 内存

Mono memory handles script objects, wrappers for Unity objects (game objects, assets, components, etc). Garbage Collector cleans up when the allocation does not fit in the available memory or on a System.GC.Collect() call.

Mono内存处理脚本对象,作为Unity对象的封装(游戏的对象、资产、组件等)。当可用内存分配不适合或 System.GC.Collect() 调用时,垃圾收集器就会清理。

Memory is allocated in heap blocks. More can allocated if it cannot fit the data into the allocated block. Heap blocks will be kept in Mono until the app is closed. In other words, Mono does not release any memory used to the OS (Unity 3.x). Once you allocate a certain amount of memory, it is reserved for mono and not available for the OS. Even when you release it, it will become available internally for Mono only and not for the OS. The heap memory value in the Profiler will only increase, never decrease.

内存是在堆块中分配的。如果无法将数据放入已分配的块中,则可以申请分配更多块。堆块将保持在Mono中,直到应用程序关闭。换句话说,Mono并没有释放任何内存到操作系统中(Unity 3.x)。一旦你分配了一定数量的内存,它就会被保留为mono,而不可用于操作系统再分配。即使你释放了它,它也只会在内部为Mono所用而不为操作系统所提供。Profiler中的堆内存值只会增加,不会减少。

If the system cannot fit new data into the allocated heap block, the Mono calls a “GC” and can allocate a new heap block (for example, due to fragmentation).

“Too many heap sections” means you’ve run out of Mono memory (because of fragmentation or heavy usage).

Use System.GC.GetTotalMemory to get the total used Mono memory.

The general advice is, use as small an allocation as possible.

如果系统不能将新数据放入已分配的堆块中,Mono调用一个“GC”,并且可以分配一个新的堆块(例如,由于存储碎片)。

“太多的堆”意味着您已经耗尽了Mono内存(因为存储碎片或大量使用)。

使用 System.GC.GetTotalMemory 获得总已使用的Mono内存。

一般的建议是,尽可能少地使用分配。

Unity memory

Unity内存

Unity memory handles Asset data (Textures, Meshes, Audio, Animation, etc), Game objects, Engine internals (Rendering, Particles, Physics, etc). Use Profiler.usedHeapSize to get the total used Unity memory.

Unity内存处理资产数据(纹理、网格、音频、动画等)、游戏对象、引擎内部(渲染、粒子、物理等)。使用 Profiler.usedHeapSize 来获得总已使用的Unity内存。

Memory map

内存映射

No tools yet but you can use the following.

  • Unity Profiler - not perfect, skips stuff, but you can get an overview. It works on the device!
  • Internal profiler. Shows Used heap and allocated heap - see mono memory. Shows the number of mono allocations per frame.
  • Xcode tools - iOS
  • Xcode Instruments Activity Monitor - Real Memory column.
  • Xcode Instruments Allocations - net allocations for created and living objects.
  • VM Tracker (textures usually get allocated with IOKit label and meshes usually go into VM Allocate).

目前还没有工具,但是您可以使用以下内容。

  • Unity Profiler ——不是完美的,跳过了一些东西,但是你可以得到一个概述。它在设备上可运行!
  • 内部 profiler。显示已使用堆和分配的堆——请参阅mono内存。显示每帧的mono分配的数量。
  • iOS Xcode工具
  • Xcode Instruments Activity Monitor-真实内存列。
  • Xcode Instruments Allocations-为创建的和存在的对象的净分配。
  • VM Tracker (纹理通常是用IOKit 标签分配的,而网格通常会由VM分配)。

You can also make your own tool using Unity API calls:

你也可以使用Unity API创建自己的工具:

  • FindObjectsOfTypeAll (type : Type) : Object[]
  • FindObjectsOfType (type : Type): Object[]
  • GetRuntimeMemorySize (o : Object) : int
  • GetMonoHeapSize
  • GetMonoUsedSize
  • Profiler.BeginSample/EndSample - profile your own code
  • UnloadUnusedAssets () : AsyncOperation
  • System.GC.GetTotalMemory/Profiler.usedHeapSize

References to the loaded objects - There is no way to figure this out. A workaround is to “Find references in scene” for public variables.

对加载的对象的引用——没有办法解决这个问题。一个解决方案是为公共变量“查找场景中的引用”。

Garbage collector

垃圾回收

  • This fires when the system cannot fit new data into the allocated heap block.
  • Don’t use OnGUI() on mobiles: it shoots several times per frame, completely redraws the view and creates tons of memory allocation calls that require Garbage Collection to be invoked.
  • 当系统不能将新数据放入已分配的堆块时,就会触发这种情况。
  • 不要在手机上使用OnGUI():它每帧触发几次,完全重新绘制视图,并创建大量的内存分配调用,需要调用垃圾收集。

Creating/removing too many objects too quickly?

过快地创建/移除太多对象

  • This may lead to fragmentation.
  • Use the Editor profiler to track the memory activity.
  • The internal profiler can be used to track the mono memory activity.
  • System.GC.Collect() You can use this .Net function when it’s ok to have a hiccup.
  • 这可能导致存储碎片。
  • 使用Editor profiler来跟踪内存活动。
  • 内部profiler 可以用来跟踪mono内存活动。
  • System.GC.Collect() 当它可以卡顿的时候,你可以使用这个.Net函数。

Allocation hiccups

分配问题

  • Use lists of preallocated, reusable class instances to implement your own memory management scheme.
  • Don’t make huge allocations per frame, cache, preallocate instead
  • Problems with fragmentation?
  • 使用预先分配的、可重用的类实例的列表来实现您自己的内存管理方案。
  • 不要在单帧进行大量的分配,缓存和预分配可替代
  • 存储碎片的问题?

Preallocating a memory pool.

预分配内存池

Keep a List of inactive GameObjects and reuse them instead of Instantiating and Destroying them.

保留一个未激活的游戏对象的列表,并重新使用它们,而不是实例化和销毁它们。

Out of mono memory

mono 内存溢出

  • Profile memory activity - when does the first memory page fill up?
  • Do you really need so many gameobjects that a single memory page is not enough?
  • Use structs instead of classes for local data. Classes are stored on the heap; structs on the stack.
  • Read the Understanding Automatic Memory Management page.
  • Profile 内存活动——第一个内存页何时填满?
  • 你真的需要这么多的游戏对象,一个内存页是不够的吗?
  • 本地数据使用structs 而不是classes。类存储在堆中;结构体在栈上。
  • 阅读理解自动内存管理页面。

Out of memory crashes

内存溢出崩溃

At some points a game may crash with “out of memory” though it in theory it should fit in fine. When this happens compare your normal game memory footprint and the allocated memory size when the crash happens. If the numbers are not similar, then there is a memory spike. This might be due to:

在某些时候,游戏可能会随着“内存不足”而崩溃,尽管理论上它应该可以很好地适应。当发生这种情况时,对比发生崩溃时和正常游戏内存占用和分配的内存大小会发生比较。如果数据不相似,那么就会有一个内存峰值。这可能是由于:

  • Two big scenes being loaded at the same time - use an empty scene

    between two bigger ones to fix this.

  • Additive scene loading - remove unused parts to maintain the memory

    size.

  • Huge asset bundles loaded to the memory
  • Textures without proper compression (a no go for mobiles).
  • Textures having Get/Set pixels enabled. This requires an uncompressed copy of the texture in memory.
  • Textures loaded from JPEG/PNGs at runtime are essentially uncompressed.
  • Big mp3 files marked as decompress on loading.
  • Keeping unused assets in weird caches like static monobehavior fields, which are not cleared when changing scenes.

    -两个大场景同时被加载 - 使用一个空的场景放在两个大场景中间过渡来解决这个问题。

    -叠加场景加载-删除未使用的部件以维护内存大小。

  • 巨大的asset bundles加载到内存中
  • 纹理没有适当的压缩(不适合移动端)。
  • 具有Get/Set像素的纹理。这需要在内存中有一个未压缩的纹理副本。
  • 在运行时从jpeg/pngs加载的纹理本质上是未压缩的。
  • 大的mp3文件被标记为在加载时解压缩。
  • 将未使用的资产保存在诸如静态monobehavior 字段之类的奇怪缓存中,这些字段在更改场景时不会被清除。

继续阅读