Unity native engine object及内存回收的碎碎念

想细细研究下 native engine object,发现之前理解有误,不过 Unity 到最后也没说清楚,所以标题从探究换成了碎碎念。
前排提醒:本文主要内容为个人思考,未精细排版,且少图多字,观感不佳。
如果你是搜索引擎过来的,建议直接跳转到重新理解原生引擎对象
标题处
起因
在 RenderTexture.Release 的文档中,Unity 官方有这么一句:
我从用 Unity 开始,对于Texture/RenderTexture
这种 C# 对象都是这么理解的:它是对原生(OpenGL等)Texture
的封装,真正的资源不是 C# 层面的东西,new 创建的资源占用的内存不会被被自动托管回收。
而 As with 这个词,一般翻译成和 xxx 一样,因此,这句话翻译成“和其他native engine object
一样”应该是没问题的。
根据这句话,我想当然的理解成“Unity 有数种对原生资源的封装类型,他们统称为 native engine object”。进一步扩展,则理解成“对于这种封装原生对象的组件,需要时刻注意管理其内存,因为他们不会像托管类型一样被垃圾回收”。
然而,当我搜索native engine object
的详细信息时,论坛上的 Unity 员工却说所有继承自UnityEngine.Object
的都是native engine object
,这让我的认知出现了混乱。
混乱根源
由于 native engine object 这三个字母太长了,下面用原生引擎对象代称。
Unity 员工原文是这么说的:
Native Unity Objects means any type that inherits from UnityEngine.Object.
Any such type can have a Managed Wrapper/Shell object used to communicate
with the native side from a script. That wrapper can get garbage collected,
but that won't cause the destruction of the native Object.
根据他的说法,所有继承自UnityEngine.Object
的类型都是原生引擎对象,也就是说,常见的GameObject
、Componment
等都是原生引擎对象。
而我一直以为Mesh
、Texture
、AudioClip
这种才是原生引擎对象,仔细想想,可能是被 native 这个单词误导了。
重新理解原生引擎对象
上面那个 Unity 员工列出了原生引擎对象的 6 种场景:
翻译过来就是:
- 场景里本来就有的对象或运行时与场景关联的对象(例如运行时创建 GameObject 并放置于当前场景中),它们将在加载其他场景卸载本场景时清理,或者在卸载场景后调用 UnlaodUnusedAssets 时被清理;
- 通过 DontDestroyOnLoad 标记持久化的对象;
- 使用 new 创建的对象;
- 访问 .material 属性获取的对象;
- 实例化的 ScriptableObjects 等;
- 创建的 Texture/RenterTexture 或其他资源;
按照他的说法,我之前只是片面的将 6 的内容理解为原生引擎对象。
尝试重新理解原生引擎对象内存回收
接下来就需要理解(或者熟悉)这段话:
翻译下其内容:
对于除了 1 之外的内容,都需要自己对其进行生命周期管理,即手动调用Object.Destroy
清理。
AddComponent
将会把组件与GameObject
关联,所以它会和GameObject
的生命周期走,可以被自动卸载(不用手动清理)
卸载场景时自动清理的逻辑:旧的场景会被破坏性卸载,用来释放空间给新场景加载使用,此时会触发Resource.UnloadUnusedAssets
。在卸载旧场景前,如果有些资源被下一个场景引用,那么卸载它们就会导致重新加载造成资源浪费。
你可以手动调用Resource.UnloadUnusedAssets
,不过如果你只有一个场景,那么与场景一起加载的内容都会在内存中(除了实例化被销毁的动态内容)。
上面那段话不难理解,条理似乎也很清晰。
迷惑的点
论坛评论中有人提到,自己创建(new)的 Mesh 通过UnloadUnusedAssets
也会被卸载。
而这就与上面说的不一致了。
老外的吐槽
老外的吐槽道出了痛点:
总的来说就是 Unity 的内存回收逻辑处于一个黑箱状态,我们只能自己去试验,且试验出来的结果与官方声称的结果可能对不上,对于和原生 C++、OpenGL 等相关的内存管理来说尤其如此,只能到最后头疼医头,脚疼医脚。
碎碎念与展望
我暂且还和原来一样,只手动处理 Mesh、Texture/RenderTexture 等的生命周期与内存释放好了,暂时不管其他东西。
至于官方,那位 Unity 员工的回复中说正在起草UnityEngine.Object
内存相关的文档(2021.11.15),那就等等看吧。如上面吐槽第一条所说,希望 Unity 给个列表,说明哪些类型需要我们特别关注。
所以说如果用了RenderTexture,但在切换场景的时候没有去手动释放这个RenderTexture就会有问题了吧