Unity3D 内存管理底层原理分析

238 阅读4分钟

前言

Unity3D 的内存管理机制涉及 托管堆(Managed Heap)  和 非托管堆(Unmanaged Heap)  两部分,分别对应 C# 代码和 Unity 引擎底层的内存管理。以下是其底层原理的详细分析:

对惹,这里有一个游戏开发交流小组 ,大家可以点击进来一起交流一下开发经验呀!

1. 托管堆(Managed Heap)

托管堆由 C# 的垃圾回收器(Garbage Collector, GC)  管理,用于存储 C# 对象(如类实例、数组等)。

内存分配

  • 当创建 class 实例或引用类型对象(如 List<int>)时,内存从托管堆分配。
  • 分配流程:
  1. 首次分配:堆内存按块(Segments)划分,每个块分为多个对象槽。
  2. 快速分配:新对象优先分配在空闲区域(称为“分配指针”位置)。
  3. 空间不足时:触发垃圾回收(GC)或扩展堆内存(向操作系统申请新内存块)。

垃圾回收(GC)

  • 分代回收机制:托管堆分为 0代(年轻代)、1代(中间代)、2代(老年代)

    • 新对象分配在 0 代。
    • 若对象在一次 GC 后存活,则晋升到下一代。
    • 0 代 GC 最频繁(速度快),2 代 GC 最耗时(涉及全堆扫描)。
  • GC 流程

  1. 标记阶段:从根对象(静态变量、活动线程栈等)出发,标记所有可达对象。
  2. 清除阶段:回收未被标记的对象内存。
  3. 压缩阶段(可选):整理内存碎片,合并空闲空间(可能影响性能)。

托管堆的问题

  • 内存碎片:频繁分配/释放可能导致碎片化,降低内存利用率。
  • GC 卡顿:2 代 GC 会导致主线程暂停(影响帧率)。

2. 非托管堆(Unmanaged Heap)

非托管堆由 Unity 引擎底层(C++)  管理,存储引擎资源(如纹理、网格、音频等)和 Native 代码分配的内存。

内存分配

  • Unity 通过 引用计数(Reference Counting)  或 显式释放 管理非托管资源。

    • 例如:Texture2DGameObject 等 UnityEngine.Object 子类。
  • Native 内存分配:通过 C/C++ 的 malloc/new 或 Unity API(如 Texture2D.Create)分配。

回收机制

  • 引用计数

    • 当 UnityEngine.Object 的引用计数归零时,引擎标记为可回收。
    • 实际释放时机由引擎决定(可能延迟到帧末尾或场景切换)。
  • 显式释放

    • 调用 Resources.UnloadUnusedAssets() 或 AssetBundle.Unload(true)
    • 使用 Destroy 或 DestroyImmediate 销毁对象(后者立即释放,但需谨慎使用)。

3. Unity 内存管理的核心问题

托管与非托管的交互

  • 托管对象引用非托管资源:例如 Texture2D 在 C# 层是托管对象,但实际数据存储在非托管堆。
  • 内存泄漏:若托管层代码持有对非托管资源的引用(如未释放 AssetBundle),会导致资源无法回收。

常见性能问题

  1. 频繁 GC 触发:在 Update 中频繁分配堆内存(如 new 对象、字符串拼接)。
  2. 非托管资源泄漏:未正确卸载 AssetBundle 或未销毁 UnityEngine.Object
  3. 内存碎片:大量小对象分配导致托管堆碎片化。

4. 优化策略

减少 GC 压力

  • 避免频繁堆分配

    • 使用对象池(Object Pool)复用对象(如子弹、粒子特效)。
    • 使用 struct 替代 class(值类型分配在栈上,不触发 GC)。
    • 避免在循环中分配内存(如缓存集合、预分配数组)。
  • 控制 GC 触发时机

    • 手动调用 System.GC.Collect()(建议在加载场景时触发)。

管理非托管资源

  • 及时释放资源

    • 调用 Resources.UnloadUnusedAssets() 释放未引用资源。
    • 销毁不再使用的 GameObjectDestroy)或释放 AssetBundle
  • 引用管理

    • 避免静态变量长期持有资源引用。

工具辅助

  • Unity Profiler:监控内存使用(Memory > Detailed 视图)。
  • Heap Explorer:分析托管堆碎片和对象分布。
  • Addressables:替代 Resources 系统,更灵活管理资源生命周期。

5. 底层机制补充

  • IL2CPP 的影响:Unity 将 C# 代码转换为 C++ 时,托管堆的 GC 由 Boehm GC 或 增量式 GC(2021+ 版本)管理,优化了跨平台性能。
  • Job System 与 Burst:使用 Unity 的 ECS 和 Burst 编译器可减少托管堆分配,提升性能。

总结

Unity 的内存管理是托管与非托管的混合模式,开发者需同时关注 C# 层的 GC 行为和引擎资源生命周期。通过减少堆分配、复用对象、及时释放资源,并结合 Profiler 分析,能有效优化内存使用,避免卡顿和泄漏。

更多教学视频

Unity3D​www.bycwedu.com/promotion_channels/2146264125