元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Unity脚本执行机制
▶
执行流程
主线程与游戏循环
事件函数执行顺序
脚本编译管线
▶
运行时环境
Mono与IL2CPP对比
垃圾回收机制
值类型与引用类型内存布局
▶
渲染管线剖析
▶
架构设计
SRP核心架构
BatchRendererGroup原理
GPU Instancing实现
▶
优化策略
动态合批与静态合批
剔除优化原理
LOD系统实现
▶
物理引擎原理
▶
核心架构
PhysX集成原理
碰撞检测算法
关节系统实现
▶
性能优化
空间划分策略
多线程物理模拟
固定时间步长原理
▶
内存管理体系
▶
内存分配
Native内存管理
托管堆扩展机制
内存碎片处理
▶
资源生命周期
AssetBundle卸载策略
对象池实现原理
资源引用追踪
发布时间:
2025-03-23 08:43
↑
☰
# Unity垃圾回收机制 本文将深入探讨Unity中的垃圾回收(GC)机制,帮助开发者理解内存管理的原理和优化方法。通过本文,你将了解到Unity的GC工作原理、常见问题以及如何编写GC友好的代码。 ## GC基础概念 ### 什么是垃圾回收 垃圾回收是自动管理内存的机制,主要负责: 1. 内存分配 - 为新对象分配内存 - 管理内存池 - 处理内存碎片 2. 内存回收 - 识别未使用对象 - 释放无用内存 - 压缩内存空间 3. 内存监控 - 跟踪内存使用 - 检测内存泄漏 - 优化内存分配 ### GC触发时机 Unity的GC会在以下情况触发: 1. 自动触发 - 内存达到阈值 - 系统内存压力大 - 定期清理 2. 手动触发 - 调用GC.Collect() - 场景切换 - 资源卸载 ## GC工作原理 ### 标记-清除算法 ```csharp // GC工作流程示例 public class GCExample : MonoBehaviour { private List<GameObject> activeObjects; private WeakReference cacheRef; void Start() { // 1. 分配内存 activeObjects = new List<GameObject>(); // 2. 创建弱引用 var cache = new Dictionary<string, object>(); cacheRef = new WeakReference(cache); // 3. GC会自动清理未引用对象 cache = null; } void OnDestroy() { // 4. 清理引用 activeObjects.Clear(); activeObjects = null; } } ``` 工作流程: 1. 标记阶段 - 从根对象开始遍历 - 标记可达对象 - 识别未引用对象 2. 清除阶段 - 回收未标记对象 - 更新内存布局 - 处理弱引用 ### 分代回收 ```csharp // 分代示例 public class GenerationalGC { // 第0代对象(临时对象) private void ProcessTemporary() { var temp = new object(); // 使用后立即释放 temp = null; } // 第1代对象(短期存活) private List<int> shortLived; private void ProcessShortTerm() { shortLived = new List<int>(); // 可能存活几次GC } // 第2代对象(长期存活) private static Dictionary<string, object> longLived; private void ProcessLongTerm() { if (longLived == null) { longLived = new Dictionary<string, object>(); } // 程序整个生命周期都存在 } } ``` 分代特点: 1. 第0代 - 新创建对象 - 生命周期短 - 回收频繁 2. 第1代 - 存活过一次GC - 中等生命周期 - 回收较少 3. 第2代 - 长期存活对象 - 生命周期长 - 很少回收 ## 性能优化 ### 减少GC压力 ```csharp // 优化示例 public class GCOptimization : MonoBehaviour { // 1. 对象池 private class ObjectPool<T> where T : new() { private Queue<T> pool = new Queue<T>(); private int maxSize; public ObjectPool(int size) { maxSize = size; for (int i = 0; i < size; i++) { pool.Enqueue(new T()); } } public T Get() { return pool.Count > 0 ? pool.Dequeue() : new T(); } public void Return(T item) { if (pool.Count < maxSize) { pool.Enqueue(item); } } } // 2. 结构体优化 private struct OptimizedData { public int id; public Vector3 position; public Quaternion rotation; public OptimizedData(int id, Vector3 pos, Quaternion rot) { this.id = id; this.position = pos; this.rotation = rot; } } // 3. 缓存优化 private List<OptimizedData> dataCache; private ObjectPool<GameObject> gameObjectPool; void Start() { // 预分配容量 dataCache = new List<OptimizedData>(100); gameObjectPool = new ObjectPool<GameObject>(50); } void Update() { // 避免每帧分配 for (int i = 0; i < dataCache.Count; i++) { ProcessData(dataCache[i]); } } } ``` 优化策略: 1. 减少分配 - 使用对象池 - 预分配容量 - 重用对象 2. 避免装箱 - 使用结构体 - 泛型约束 - 值类型参数 3. 合理缓存 - 缓存组件引用 - 复用临时对象 - 延迟初始化 ### 内存管理 ```csharp // 内存管理示例 public class MemoryManager : MonoBehaviour { // 1. 资源管理 private Dictionary<string, WeakReference> assetCache = new Dictionary<string, WeakReference>(); // 2. 引用计数 private class RefCountedObject { private int refCount = 0; private UnityEngine.Object target; public void AddRef() { refCount++; } public void Release() { refCount--; if (refCount <= 0) { Destroy(); } } private void Destroy() { if (target != null) { UnityEngine.Object.Destroy(target); target = null; } } } // 3. 内存监控 private void MonitorMemory() { long totalMemory = GC.GetTotalMemory(false); if (totalMemory > 1024 * 1024 * 100) // 100MB { // 触发清理 CleanupMemory(); } } private void CleanupMemory() { // 清理缓存 var deadKeys = new List<string>(); foreach (var pair in assetCache) { if (!pair.Value.IsAlive) { deadKeys.Add(pair.Key); } } foreach (var key in deadKeys) { assetCache.Remove(key); } // 强制GC System.GC.Collect(); System.GC.WaitForPendingFinalizers(); Resources.UnloadUnusedAssets(); } } ``` 管理策略: 1. 资源管理 - 使用弱引用 - 及时释放 - 延迟加载 2. 引用计数 - 跟踪使用 - 自动清理 - 避免循环引用 3. 监控清理 - 定期检查 - 阈值控制 - 主动释放 ## 调试工具 ### Memory Profiler ```csharp // 内存分析示例 public class MemoryProfiler : MonoBehaviour { private System.Diagnostics.Stopwatch stopwatch; private List<MemorySnapshot> snapshots; [System.Serializable] private class MemorySnapshot { public long timestamp; public long totalMemory; public int gcCount; public string tag; public MemorySnapshot(string tag) { this.tag = tag; this.timestamp = System.DateTime.Now.Ticks; this.totalMemory = GC.GetTotalMemory(false); this.gcCount = GC.CollectionCount(0); } } void Start() { stopwatch = new System.Diagnostics.Stopwatch(); snapshots = new List<MemorySnapshot>(); } public void TakeSnapshot(string tag) { snapshots.Add(new MemorySnapshot(tag)); } public void AnalyzeMemory() { for (int i = 1; i < snapshots.Count; i++) { var prev = snapshots[i - 1]; var curr = snapshots[i]; var memoryDelta = curr.totalMemory - prev.totalMemory; var timeDelta = (curr.timestamp - prev.timestamp) / 10000; // ms var gcDelta = curr.gcCount - prev.gcCount; Debug.Log($"Memory Analysis:\n" + $"Tag: {curr.tag}\n" + $"Memory Delta: {memoryDelta / 1024}KB\n" + $"Time Delta: {timeDelta}ms\n" + $"GC Count Delta: {gcDelta}"); } } } ``` 调试功能: 1. 内存快照 - 记录状态 - 对比变化 - 分析趋势 2. 性能分析 - 测量耗时 - 统计GC - 识别瓶颈 3. 泄漏检测 - 跟踪分配 - 引用分析 - 对象计数 ## 最佳实践 ### 编码规范 ```csharp // 最佳实践示例 public class BestPractices : MonoBehaviour { // 1. 避免字符串连接 private void LogMessage(string category, string message) { // 不好的方式 // Debug.Log(category + ": " + message); // 好的方式 Debug.Log($"{category}: {message}"); } // 2. 使用StringBuilder private string BuildReport(List<string> items) { var sb = new StringBuilder(); foreach (var item in items) { sb.AppendLine(item); } return sb.ToString(); } // 3. 缓存组件 private Transform cachedTransform; private Rigidbody cachedRigidbody; void Awake() { cachedTransform = transform; cachedRigidbody = GetComponent<Rigidbody>(); } // 4. 使用对象池 private ObjectPool<ParticleSystem> particlePool; void Start() { particlePool = new ObjectPool<ParticleSystem>(10); } // 5. 避免空检查 private void ProcessObject(GameObject obj) { if (obj == null) return; // 处理逻辑 } } ``` 关键点: 1. 字符串处理 - 使用StringBuilder - 避免+连接 - 使用字符串内插 2. 组件访问 - 缓存引用 - 避免查找 - 合理初始化 3. 集合操作 - 预分配容量 - 重用容器 - 避免调整大小 ### 常见陷阱 1. 闭包陷阱 ```csharp // 错误示例 private void CreateClosures() { for (int i = 0; i < 10; i++) { StartCoroutine(DoSomething(i)); } } // 正确示例 private IEnumerator DoSomething(int index) { yield return new WaitForSeconds(1); Debug.Log($"Index: {index}"); } ``` 2. 协程陷阱 ```csharp // 错误示例 IEnumerator BadCoroutine() { while (true) { var info = new object(); // 每次迭代都分配 yield return null; } } // 正确示例