在Unity的内存管理机制中, Reserved Total 和 Used Total之间的关系是怎样的?

本文提供了游戏性能优化的具体建议,包括DrawCall数量、Triangle数量、可见蒙皮网格数量、Contacts数量、Rigidbody数量、碰撞体数量的推荐值,以及纹理资源优化技巧。针对不同平台如Mobile、Android、iOS给出了具体的优化策略。

Draw Call数量、Triangle数量 和 可见蒙皮网格数量

一般来说,Draw Call 数量Triangle 数量可见蒙皮网格数量 的推荐值需根据平台的不同而不同。对于 Mobile 低端移动设备来说,建议 Draw Call 数量的主要范围在 [0,200] 区间内,Triangle 数量保持在 10万 以下,可见蒙皮网格数量保持在 50 以下。

Contacts 数量、Rigidbody 数量 和 碰撞体数量

Contacts 数量为碰撞对(Contact Pair)数量。所谓“碰撞对”,是指当前空间中发生碰撞的物体之间的接触点,每个 “碰撞对” 均连接两个相互产生碰撞的物体。一般来说,Contacts 数量越大,则表明碰撞物体的数量越多,即物理系统的CPU开销越大。

Rigidibody数量 和 碰撞体数量, 其标准值需因运行平台的不同而不同。对于Mobile移动设备来说,建议 Rigidibody数量 控制在 50 以下,碰撞体数量(静态碰撞体和动态碰撞体) 控制在 100 以下。

贴图资源

在纹理资源的优化方面,建议尽可能降低其内存上的占用,其主要影响因素包括纹理分辨率、纹理格式、Mipmap等。对于纹理格式,则尽可能采用适合于发布平台的硬件支持纹理格式,比如对于Android平台,建议使用ETC1格式,对于iOS平台,建议使用PVRTC格式,对于PC,建议使用DXT格式等等。

注意:在Android平台上,ETC1格式不支持半透明纹理,所以半透明纹理可以直接转换成RGBA16格式进行渲染。当然,更为出色的办法是,将半透明纹理拆分成两张纹理,记录RGB通道信息的RGB24格式纹理和记录Alpha通道信息的Alpha8格式纹理,然后再分别转成ETC1格式进行保存。这种方法既可以最大限度地保证渲染质量,又可以极大降低纹理内存的占用。
纹理从RGBA32转换成RGBA16、ETC1或PVRTC格式时,均会带来一定程度的质量损失,甚至出现难以接受的 “色阶” 效果。究其原因,是因为纹理图像中的过渡色所致。因此,建议您在显示效果和性能效率方面进行权衡,尽可能降低过渡色的跨度范围

“多余” 数据的危害还体现在网格拼合上。Unity 引擎的 Draw Call Batching 功能或您自己调用的 StaticBatchingUtility API 均可以将场景中的网格数据进行拼合,从而形成一个或几个较大的 VBO(Combined Mesh)。这种做法是渲染优化时的常见方法,主要用于降低渲染模块的Draw Call数量,从而提升渲染效率。这本身是一个非常棒的功能,但您需要注意的是,网格合并时一定要保证网格数据结构的统一性,否则会大大增加拼合网格(Combined Mesh)的数据量!举个例子,当拼合100个网格模型时,如果99个模型网格数据中仅含有Vertex、UV等数据,而仅有1个模型网格数据中含有Vertex、UV、Normal、Color和Tangent数据,则拼合后,拼合网格中的所有顶点均会添加上 Normal、Color 和 Tangent 数据,大量的 “多余” 数据也就由此产生了...

总内存和使用内存

Reserved TotalUsed Total 为Unity引擎在内存方面的总体分配量和总体使用量。 一般来说,引擎在分配内存时并不是向操作系统 “即拿即用”,而是首先获取一定量的连续内存,然后供自己内部使用,待空余内存不够时,引擎才会向系统再次申请一定量的连续内存进行使用。Reserved Total 的内存占用量略大于 Used Total, 且两者走势基本一致。

注意:对于绝大多数平台而言,Reseved Total内存 = Reserved Unity内存 + GFX内存 + FMOD内存 + Mono内存

通过针对大量项目的深度分析,我们发现导致 Reserved Unity 内存分配较大的原因主要有以下几种:

  1. WebStream内存占用:WebStream为项目通过特定API(WWW、CreateFormMemory等)加载AssetBundle文件所开辟的较大块内存。主要用于存放AssetBundle的原始数据和解压后数据。
  2. 序列化信息内存占用:Unity引擎的序列化信息种类繁多,其中最为常见且内存占用较大的为SerializedFile。该序列化信息的内存分配主要为项目通过特定API(WWW.LoadFromCacheOrDownload、CreateFromFile等)加载AssetBundle文件所致。
  3. 资源内存占用:主要包括Mesh、AnimationClip、RenderTexture等资源。在默认情况下,当FBX模型导入时,其 “Read/Write Enable” 选项是默认开启的,即其Mesh网格数据会在 Reserved Unity 中保留一份,便于项目在运行时对Mesh数据进行实时的编辑和修改。同时,如果研发团队同样开启了纹理资源的 “Read/Write Enable” 选项(默认情况下为关闭),则纹理资源同样会在 Reserved Unity 中保留一份,进而造成其更大的内存占用。

GFX内存 为底层显卡驱动所反馈的内存分配量,该内存分配由底层显卡驱动所控制。一般来说,该部分内存占用主要由渲染相关的资源量所决定,包括纹理资源、Mesh资源、Shader资源以及解析这些资源的相关库所分配的内存等。

托管堆内存 表示项目运行时代码分配的托管堆内存分配量。对于使用Mono进行代码编译的项目,其托管堆内存主要由Mono分配和管理;对于使用IL2CPP进行代码编译的项目,其托管堆内存主要由Unity自身分配和管理。目前,除iOS平台外,其他平台的绝大多数项目还在使用Mono来进行代码编译。
对于Mono堆内存来说,由于Mono自身的限制,其堆内存分配是 “只升不降” 的,即内存一旦分配给Mono,不论以后该内存是否继续被使用,都不会再归还给系统。因此,建议您对于代码的堆内存分配进行严格的控制,避免不必要的Mono堆内存分配。

Instantiate实例化操作

一般情况下,我们不建议项目在运行过程中频繁地调用 Instantiate 和 Destroy 来创建和销毁一个GameObject。这种做法不仅可能会造成频繁且较高的CPU占用,更重要的是,它将造成大量的内存碎片,从而造成越来越多的堆内存分配,进而加速系统垃圾回收操作(Garbage Collection)的到来





<think>嗯,用户遇到了Unity运行时内存分配失败的问题,错误提示是“TempOverflowAllocation hashset.hLine1008”。我需要先理解这个错误的背景可能的原因。首先,用户提到的错误位置在hashset.h的第1008行,这可能涉及到Unity内部的内存管理机制。TempOverflowAllocation听起来像是临时内存分配的溢出,可能是在处理某些资源时超出了预设的内存限制。我需要考虑几个可能的原因:内存泄漏、资源加载过大、内存碎片,或者Unity的临时分配池设置不合理。接下来,参考用户提供的引用,虽然引用内容主要是关于构建后的资源大小查看、调试工具Shader编译,但可以从中提取一些相关点。例如,引用[1]提到在构建后查看Temp目录下的数据大小,这可能帮助用户分析内存使用情况。引用[3]提到Shader编译时的资源处理,如果Shader编译过程中占用了过多内存,也可能导致运行时内存不足。 然后,解决方案需要分步骤。第一步是优化资源管理,比如检查纹理、模型、音频等资源的大小格式,是否有不必要的冗余。然后,可以考虑增加内存限制,调整Unity的GC设置,或者使用内存分析工具如Profiler来定位泄漏点。另外,用户可能需要检查临时分配池的设置。Unity的临时内存分配可能在处理大量瞬时对象时溢出,调整Allocator.Temp或Allocator.TempJob的参数可能有帮助。同时,内存碎片问题可以通过减少频繁的小内存分配来缓解。最后,需要生成相关问题,帮助用户深入理解问题。例如,如何分析内存泄漏,或者Unity内存管理机制。这些相关问题应该基于解决方案中的关键点,引导用户进一步排查问题。</think>### 解决方案:Unity运行时内存分配失败(TempOverflowAllocation错误) #### 1. **问题定位与分析** - **错误信息含义**:`TempOverflowAllocation`表示Unity的临时内存分配池(Allocator.Temp)溢出,常见于高频小对象分配或内存泄漏[^3]。 - **触发场景**:频繁使用`Allocator.Temp`或`Allocator.TempJob`的API(如`NativeArray`)、大量瞬态对象(如粒子系统)、资源加载未及时释放。 #### 2. **具体解决步骤** **步骤1:优化内存分配策略** - 减少高频小对象分配: ```csharp // 避免在循环中频繁创建对象 void Update() { // 错误示例:每帧新建数组 NativeArray<float> tempArray = new NativeArray<float>(100, Allocator.Temp); // 正确示例:复用已分配的内存 if (!_cachedArray.IsCreated) { _cachedArray = new NativeArray<float>(100, Allocator.Persistent); } tempArray.Dispose(); // 及时释放 } ``` - 使用`Allocator.Persistent`替代`Allocator.Temp`,但需手动管理生命周期。 **步骤2:检查内存泄漏** - 通过**Unity Profiler** > **Memory**模块: - 观察`Total Allocated``GC Reserved`的变化趋势。 - 检查`Native Memory`中的`TempAlloc`峰值是否持续增长。 - 使用**Memory Snapshot**对比运行前后的内存快照,定位未释放资源。 **步骤3:调整Unity内存配置** - 在`Project Settings` > **Player** > **Other Settings**中: - 增加`GC Heap Size`(需根据目标平台调整)。 - 启用`Incremental GC`减少垃圾回收卡顿。 **步骤4:资源加载优化** - 异步加载资源并限制并发量: ```csharp IEnumerator LoadAssetAsync(string path) { ResourceRequest request = Resources.LoadAsync(path); yield return request; // 使用后及时卸载 Resources.UnloadAsset(request.asset); } ``` - 使用`Addressables`或`AssetBundle`替代`Resources.Load`,实现更细粒度的控制。 #### 3. **高级排查方法** - **查看堆栈跟踪**:在错误日志中定位触发`hash set.h Line 1008`的具体代码位置。 - **调试Native代码**:通过LLDB或Visual Studio附加到Unity进程,检查`TempAlloc`的分配路径[^2]。 - **修改源码(仅限自定义引擎)**:若使用自定义Unity版本,可调整`Allocator.Temp`的池大小(需修改`MemoryManager.cpp`中的`kTempAllocatorSize`)。 --- ###
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值