Unity 内存管理

Unity 内存管理

为什么在unity项目开发中的内存占用是一个非常大的问题,那是因为一些场景和代码造成的非必要内存的占用,那么这种问题如何解决呢?下面会为大家unity中的内存管理,了解内存的种类以及对应种类的优化和使用的技巧。

三种内存

  • 程序代码
  • 托管堆(Managed Heap)
  • 本机堆(Native Heap)

程序代码

就是Unity引擎本身以及我们使用的各种库,还有自己写的程序代码,这些代码段是要载入内存才能使用的
我们要尽量减少不必要的库被打包进最终的包里

托管堆

就是C#/Mono代码使用的内存。托管堆是有GC(Garbage Collect)机制的,这使得我们不需要手动delete任何实例

新申请的空间不会被释放

需要注意的是,这一块本质上一个被虚拟机管理的内存,在我们创建Class的实例时,如果虚拟机空间不足,是会申请新的空间的,但是,这个空间不会再被释放回去就是说,托管堆只会增长不会减少,即使你的C#类的实例都被GC掉了,但虚拟机还是要占那么多的内存的
在iOS中,为了提供64的支持,Unity开启了IL2CPP项目,但是这个东西也还是一个虚拟机,我们的C#代码虽然被翻译成了C++代码,但它的内存管理逻辑和C#还是一样的

GC时回造成线程停顿

托管堆还有一个坑是GC,GC会造成线程停顿,主线程需要停下来来完成GC操作。所以,在战斗过程中,要尽量避免Managed Heap的请求和释放(主要是释放)
C#提供了值语义来实现简单的类C++RAII的概念,可以避免频繁的GC。但要注意box/unbox问题

本机堆

可以是Unity自己管理的一部分内存,主要是一些Native的资源,比如贴图,音效,关卡数据等。

手动管理方式

这部分内存我们也可以自己申请和使用的,我们使用native插件也是使用这部分内存。
在C#代码中,我们也可以申请和使用Native Heap的:

int size = 65536;//1024;
      byte* buffer = (byte*)Marshal.AllocHGlobal(size);

Marshal.aspx)是C#提供的一个直接操作Native内存的接口,通过这个系统,我们可以绕过GC系统,手动管理内存,以在某些情况下大幅优化C#的性能

自动管理方式

在Unity中,有一套自动的资源加载和卸载的机制,这使我们不需要在代码中指定任何资源的加载过程,我们想要显示的时候,它就会在哪里了,非常方便。但是,这样我们也失去了手动管理资源的权力,很容易导致Unity吃掉大量的内存
Unity的Asset使用了一套引用计数的系统来管理
这里写图片描述
当我们使用Unity的Profiler来观察内存占用情况时,我们可以看到每个Asset后面会有一个Refcount的数字,而右边列出引用了它的东西,这些一般是具体的C#的实例

如果一个Asset没有了引用,使用Resources.UnloadUnusedAssets()就可以卸载它了。使用Resource.UnloadAsset()可以强制卸载一个资源,但是如果它还有引用,它会被再次加载进来的

Unity默认会在切换Scene时自动卸载所有资源,因为所有的C#的实例都被销毁掉了

我们要在一个Scene中动态的加载和卸载资源,就要用Resource.UnloadAsset()了。尽量不要使用Resources.UnloadUnusedAssets()因为它是一个遍历查找的操作,会造成卡顿

AssetBundle加载

AssetBundleUnity提供的唯一的资源更新方法,
它的整个操作周期可以使用下图来表示:
这里写图片描述
Instantiate指的是Prefab的GameObject.Instantiate, 这个函数会实例化一个Prefab,这个Prefab所使用的Asset会根据不同的类型,进行引用或Clone或者Clone(复制)+引用的形式来产生
AssetBundle.Load就是一个反序列的过程,它会创建一个GameObject出来,这个GameObject可以用来实例化成我们真正可用,挂载在场景树中的GameObejct

AssetBundle压缩

注意上图的紫色的AssetBundleMemory部分
这里写图片描述
一般来说,我们要使用LoadFromCacheOrDownload来加载AssetBundle因为这样可以避免这部分内存占用。但是,Unity其实是把AssetBundle解压到一个Cache里然后加载的,还是会增加本地的磁盘占用的,在iOS中,这个占用还是比较麻烦的。因为LZMA压缩可以节省80%左右的磁盘占用,这意味着,解压后会放大五六倍

在Unity5.3中,引入了一个新的压缩方式LZ4,它是一个chunk-based(基于块)的压缩方式,它的好处就是,在读区时我们可以只解压指定的chunks而不必如LZMA一样,解压整个AssetBundle文件到内存

LZ4可以节省40%–60%的空间,同时实时读取解压时的性能消耗还可以接受
这里写图片描述
可以看到,LZ4版本的AssetBundle中加载prefab性能下降还是很明显的

需要注意的是,在5.3后的版本中,WWW.LoadFromCacheOrDownload默认产生的Cache,都是使用LZ4压缩的,而不是完全解压后的版本。如果你注意到AssetBundle中加载资源比Resources中慢,检查一下这个

使用LZ4可以解决iOS中,游戏占用过多磁盘空间的问题,同时带来的性能消耗还可以接受。
使用LZ4我们就可以直接使用LoadFromFile来加载AssetBundle了,而且不必调用AssetBundle.Unload(false)来释放内存空间,也不必卸载AssetBundle本身了。AssetBundle甚至可以被看作一个简单的虚拟文件系统了,只要挂上就可以了
【原文参考】Unity 内存管理
【原文参考】全面理解Unity加载和内存管理
【原文参考】全面理解Unity加载和内存管理机制之二:进一步深入和细节

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值