Unity 托管内存(Managed Memory)

翻译:https://unity3d.com/cn/learn/tutorials/topics/best-practices/managed-memory

理解托管堆对 Unity 中的内存管理至关重要。 有关分析托管内存和如何优化内存的更多信息,请参阅了解 Unity 中的理解优化部分。

一、Assets

资产在运行时影响本机内存和托管内存。 在用户终止应用程序之前,Unity 运行时不会将托管内 存返回到操作系统进行重用。 如果托管堆变得过于支离破碎并耗尽可用内存,则它会增大。 由于这种不可预测的行为,知道资产如何占用托管内存是非常重要的:

  • 使用Destroy(myObject)来销毁一个对象并释放其内存。 将对象的引用设置为 null 并不会破坏它
  • 您应该将持久(long-term)对象设置为类,将临时(short-term)对象设置为结构。 结构不在堆上分配,因此不会进行垃圾回收。
  • 重用临时工作缓冲区以保持临时垃圾较低,而不是频繁分配。
  • 枚举数在退出之前不会清除其内存。
  • 您应该避免永无止境的协程,尤其是在 on 中分配大量托管内存时,因为协程会保持堆上的堆栈分配,直到协程结束。
    请阅读 Unity优化指南中的理解托管内存部分,以获得对托管堆(的更深入的理解。

二、IL2CPP & Mono

在 iOS 和 Android 上,在Player Settings 中选择 Mono 或 IL2CPP Scripting Backend。 要改变Scripting Backend,转到Player Settings窗口(菜单: 编辑项目设置播放器) ,向下滚动到其他设置部分,并选择 Mono 或 IL2CPP 从Scripting Backend下拉菜单。
注意: 从2017.3开始,要么选择 IL2CPP Scripting Backend,要么选择 Mono Scripting Backend。 然而,WebGL 和 UWP 都只支持 IL2CPP。 iOS 仍然支持 Mono Scripting Backend来进行快速迭代,但是你不能再向苹果提交 Mono (32位)应用程序了。

不同Scripting Backend的优缺点:
每一个脚本后端都有其优缺点,这些优缺点会影响你的决定,哪一个是适合你的情况的正确选择:

IL2CPP

  • 与 Mono 相比,代码生成得到了很大的改进。
  • 从上到下调试 c + + 中的脚本代码是可能的。
  • 您可以启用Engine code stripping以减少代码大小。
  • 构建时间比使用 Mono 要长。
  • 只支持Ahead of Time(AOT)编译。

Mono

  • 比 IL2CPP 更快的构建时间。
  • 由于Just In Time compilation(JIT) ,支持更多的托管库。
  • 支持运行时代码执行。
  • 必须装运管理的组件(.dll files that mono- or .net- produces)。

提示: 您应该使用 IL2CPP 来开发和发布您的项目。 如果使用 IL2CPP 的迭代次数太慢,那么在开发过程中临时切换到 Mono Scripting Backend,以提高迭代速度。
注意: Player Settings中的默认目标架构是为发布版本而优化的。 在开发过程中使用这个默认值会增加你的构建时间,因为 Unity 会为所选择的每个目标架构构建二进制文件:
Android Player Settings中默认的目标架构是 armv7和 x86,带有 IL2CPP 和 Mono Scripting Backend
iOS Player Settings中的默认架构是 armv7和 arm64,带有 IL2CPP Scripting Backend

Code stripping in Unity:
代码大小直接影响磁盘空间和运行时内存。 很重要的一点是,Unity 会从代码库中删除任何您没有使用的代码路径。 在构建过程中,Unity 自动条带化代码,在两个不同的层次上工作:
1.Managed code stripping
Unity 条带方法级别上的托管代码。 要改变剥离级别,转到播放机设置窗口,向下滚动到其他设置部分,找到剥离级别下拉菜单并选择剥离程序集。
Unitylinker 从中间语言(IL)中删除未使用的类型(类、结构等)。 即使您使用一个类型,UnityLinker 也会删除该类型未使用的方法。
注意: 虽然这个功能在使用 Mono 脚本后端的构建中是可选的,但是它总是在使用 IL2CPP 脚本后端的构建中启用。
2.Native code stripping
Unity 在播放机设置中默认启用条形码引擎代码,并启用本机代码剥离。 启用"条带引擎代码"以删除本机 Unity 引擎代码中未使用的模块和类。 禁用条形引擎代码以保留本机 Unity 引擎代码中的所有模块和类。
注意: 对于公开可用的平台,本地代码剥离只支持 iOS、 WebGL 和 Android。
Unity 2017.3以后支持 Android 上的本地代码剥离; 在以前的版本中,Unity Android 运行时作为预链接发布。 所以图书馆,Unity 不能剥离。 2017.3发布的 Android 运行时是一个静态引擎代码库,支持本地代码剥离。 最后一个链接发生在构建过程中,这也是构建时间稍长的原因

Unity Module Stripping
注意: WebGL 是目前唯一支持剥离未使用的 Unity 模块的平台。
Unity 尽最大努力消除所有未使用的 Unity 模块。 这意味着,如果任何场景使用或任何脚本引用了你在构建中包含的 Unity 模块中的一个组件,Unity 不会剥离该模块。 Unity 不会去掉核心模块,比如 Camera,AssetBundle,Halo 等,但是在将来的版本中,Unity 也会去掉这些模块。
从WebGL上的一个空项目中剥离模块
删除模块可以节省大量内存。 例如,Unity 中最大的模块之一是物理模块,它占用了约5mb 的带有 gzipped 的 ASM.js 代码。 如果您删除一个空项目的物理模块,它会将构建大小从17 MB 减少到12 MB。

C# Code Stripping
Unitylinker 使用基本的标记和清除原则,类似于垃圾收集器。 Unitylinker 生成包含在生成的每个程序集中的每种类型和方法的映射。 Unitylinker 将一些类型和方法标记为"根",然后 UnityLinker 遍历类型和方法之间的依赖关系图。
例如,如果一个类型的方法调用另一个类型的方法,那么 Unity Linker 将标记被调用的类型和正在使用的 methodas。 一旦 UnityLinker 标记了所有根的依赖项,系统就会重组程序集,省略未标记为使用的方法或整个类型。

1.场景、资源、程序集和资产包的根
如果 UnityLinker 的内部类在场景中使用过,或者从参考资料中的内容中使用过,它就会将它们标记为词根。 类似地,UnityLinker 将用户程序集中的所有类型和方法标记为根。
如果您直接在场景或资源中包含的资产中使用来自其他程序集的类型和方法,Unity 将它们标记为根。
使用 link.xml 文件将其他类型和方法标记为根。 如果您的项目使用 AssetBundles,那么也可以使用 buildplayeroption.asssetbundlemanifestpath 将其他类型和方法标记为根。

2.用户程序集
用户程序集是 Unity 从 Assets 文件夹中的松散代码生成的程序集。 Unity 将大部分 c # 代码放在 Assembly-CSharp。 而 Unity 将代码放在 / Assets / standard Assets / 或 / Assets / plugins / 中的 Assembly-CSharp-firstpass.dll 中,后者也被视为用户程序集。
如果有相当一部分代码库类型或方法未被使用,您可以通过将稳定代码迁移到预先构建的程序集中,并允许 UnityLinker 删除它们,从而节省一些二进制大小和构建时间。 使用程序集定义文件将稳定代码迁移到预生成的程序集中。
Generic Sharing
对于引用类型,IL2CPP 生成可以使用引用类型在泛型之间共享的实现(c + + 代码)。 然而,IL2CPP 不共享值类型,因为 IL2CPP 需要为每个类型分别生成代码。 这会导致代码大小增加。
一般来说,不应该有任何明显的性能差异,但这取决于特定的用例以及应该为什么进行优化。 类通常在堆上,而结构则在堆栈上(除了一些例外,例如协程)。 对于内存性能和使用而言,这很重要,而使用非引用类型会导致其他问题。 必须使用值类型复制函数参数以影响性能。 更多信息请看这篇博客文章。 但是要注意,Integer 或者 Enum 类型目前还没有被共享。

Assembly Definition Files
程序集定义文件允许您定义自定义托管程序集,并根据每个文件夹为它们分配用户脚本。
反过来,这会导致更快的迭代时间,因为 Unity 将只构建那些实际上受到脚本更改影响的程序集。
注意: 虽然多个程序集确实实现了模块化,但它们也增加了应用程序的二进制大小和运行时内存。 测试表明,可执行文件每个程序集最多可增长4kB。

Build Report
构建报告是一个包含在 Unity 中但是没有用户界面的 API。 构建一个项目会生成一个构建 / 挖掘文件,它可以让你发现什么被删除了,以及为什么从最终的可执行文件中删除了它。
要预览剥离信息:

  • 建立你的项目。
  • 打开编辑器。
  • 连接到 http://files.unity3d.com/build-report/ 。

构建报告工具连接到正在运行的 Unity 编辑器,下载并显示构建报告的分解。
可以在 library / latestbuild 生成的文件上使用 binary2text 工具。 可以查看报告中的数据。 Binary2text 在 Unity.app / contents / Tools / on Mac 或 Unity / editor / data / Tools / on Windows 下附带 Unity。 构建报告在 Unity 5.5及以后版本中可用。

你可能感兴趣的:(Unity3D)