垃圾回收 GC(Garbage Collection)

Unity 内存:https://docs.unity3d.com/Manual/performance-memory-overview.html

Unity游戏开发 - 内存篇:https://zhuanlan.zhihu.com/p/533047433

GC:https://en.wikipedia.org/wiki/Tracing_garbage_collection

Unity Boehm GC:https://en.wikipedia.org/wiki/Boehm_garbage_collector

Unity增量GC:https://docs.unity3d.com/Manual/performance-incremental-garbage-collection.html

深入探索垃圾收集:https://blog.youkuaiyun.com/yuff100/article/details/137789691

Lua5.3版GC机制理解:https://blog.youkuaiyun.com/BigBrick/article/details/85317491

GS GC:http://gsdocs.g-bits.com:3000/#/Reference/Implementation/Doc_GC

CLR GC:https://learn.microsoft.com/zh-cn/dotnet/standard/garbage-collection/fundamentals

为什么要GC

  • 代码创建对象等需要分配内存,对象不需要的时候,就变成内存垃圾,在有限的内存里,在不断分配内存,如果不清理掉内存垃圾,留出空间,会导致撑爆内存,程序崩溃。

  • 通常会指定一个阈值,内存分配达到阈值,就会gc一下清理下空间。

Unity的内存

在这里插入图片描述

GC算法

引用计数法

  • 项目内Unity AB加载用了这种方式。

  • 优点:实现简单,不用的对象可以及时清除。

  • 缺点:无法解决循环引用问题。

    • 对象B引用了对象C,B的计数为1。对象C同时引用了对象B,C的计数为1。实际两对象都不需要的时候也无法回收,因为计数不为0。
  • 实现:给对象加一个计数器,每次对象被一个别的对象引用了,就加1,取消引用就减1。

    • 比如A被B引用,计数为1,A又被C引用,计数为2,当B和C卸载了,A的应用计数为0,这时候就可以清除掉了。

标记-扫描法(Mark-Sweep)

  • Unity(Mono 和 IL2CPP)、CLR、GS 的垃圾回收核心算法都是这种。

  • 优点:算法简单,可以在清理时统一优化内存。

  • 缺点:这个过程需要暂停整个系统,避免这个过程引用变化导致错误回收。表现上就是系统卡住了。
    请添加图片描述

  • 实现分两个阶段:

    1. 标记:把整个“根集”(直接或间接引用托管堆中对象的指针,比如静态字段、局部变量、参数等)进行树状遍历,并将根指向的每个对象标记为 “使用中”。这些对象指向的所有对象等也都会被标记,通过根集可以到达的每个对象都会被标记。

    2. 扫描:所有内存将被从头至尾扫描一遍,检查所有空闲或已使用的区块;未标记为 "未使用 "的区块将无法通过任何根到达,其内存将被释放。对于标记为 "使用中 "的对象,"使用中 "标志会被清除,为下一个循环做准备。

三色标记法

  • Lua 用的这种算法。

  • 优点:每个阶段都可以分开执行,避免卡顿。

  • 缺点:算法复杂,内存开销较高。
    请添加图片描述

  • 实现:

    • 三种颜色代表三种状态:

      • 白色:可回收。

      • 灰色:对象被GC访问过,但这个对象引用的其他对象还没访问过。

      • 黑色:对象被GC访问过,引用的其他对象也被标记了灰或黑。不可回收。

    • 步骤:

      1. 初始都为白色。

      2. 遍历根集,标记为灰色。

      3. 遍历灰色集合,把灰色对象a引用的白色对象也挪到灰色去。把遍历过的对象a挪到黑色集合。

      4. 重复上面步骤,直到灰色集合为空。

      5. 把白色的清理掉。

    • 在gc步骤开始后,如果新增对象,对象将标记为灰色。

内存压缩

  • 可以避免内存碎片,避免出现想要分配大块内存时没有连续空间,也方便分配新的内存。

  • CLR的GC算法中,在标记清理完无引用的对象后,会更新所有对象指针,移动到一起,下次分配内存直接从最后free的指针开始分配。

  • Unity 和 Lua 都不会压缩空间,好处是省去压缩空间导致的额外时间消耗。
    在这里插入图片描述
    在这里插入图片描述

分代 GC

  • 主要减少每次GC检测对象频次。

  • Unity 采用 Boehm GC,不支持分代。

  • Lua GC三色标记法本身可以分阶段处理,没有分代。

CLR 分代

在这里插入图片描述
在这里插入图片描述

  • CLR的分代,简单理解为每次gc存活的对象会留到下一代,减少GC检测的频次,内存不够时再一起GC老一代:

    1. 第0代:这一代是最年轻的,其中包含短生存期对象。 短生存期对象的一个示例是临时变量。 垃圾回收最常发生在此代中。一般对象创建都在这,除了大对象(LOH)(超过85000字节)会直接分配到第2代

    2. 第1代:这一代包含短生存期对象并用作短生存期对象和长生存期对象之间的缓冲区。

    3. 第2代:这一代包含长生存期对象。 长生存期对象的一个示例是服务器应用程序中的一个包含在进程期间处于活动状态的静态数据的对象。

Unity 增量GC

  • 将原本一次性GC分配到多帧处理,减少每次GC时耗时峰值。

  • 写障碍:因为不是一次性GC,如果中间有引用变化,就要缺点是否需要重新扫描对象,会造成额外开销。

  • 如果对象引用变化频繁,标记阶段每次都要到下一次迭代重新扫描,最后还是会变成非增量收集。

Unity Profiler

  • 可以查看触发GC时耗时;和每帧产生托管堆内存大小。

在这里插入图片描述

### 垃圾回收器工作原理 垃圾回收器的主要目标是在程序运行期间自动管理和释放不再使用的内存。其基本思路是识别并回收那些不再可访问的对象所占用的空间。 #### 标记-清除算法 一种经典的垃圾回收策略被称为「标记-清除」(Mark-Sweep),该方法分为两个主要阶段: - **标记阶段**:遍历所有根节点(通常是栈中的引用),递归地标记所有可达对象。 - **清理阶段**:扫描堆空间,查找未被标记的对象,并将其对应的内存区域回收以供后续再利用[^4]。 此过程中,任何未能通过可达性分析的对象都将被认为是废弃的,进而得到处理。 ### 分代收集机制 为了提升效率,现代JVM采用分代假设理论设计了多级结构化的垃圾收集体系——即所谓的“世代模型”。这种架构下,新生代、老年代各自对应不同频率与方式的清扫动作: - 新生代(Young Generation)通常发生较为频繁的小规模回收事件(Minor GC)。由于大多数新创建的对象很快就会变得不可达,因此这部分内存会被快速周转。 - 当某些存活周期较长的对象经过多次年轻代GC后仍未被淘汰,则迁移到年老代(Old Generation)。针对后者开展的大范围整理行动称为Major/Mixed GC,这类操作相对少见却耗时更久[^1]。 值得注意的是,在Java 8之后版本里,原本用于存储类元数据信息的永久代已被替换成了名为Metaspace的新组件;当这个区域容量不足时同样会引起Full GC的发生[^3]。 ### C# 中的具体实践 对于.NET平台而言,开发者应当注意合理控制应用程序内的对象生命期长度,比如优先选用局部作用域内的临时实例而非长期存在的字段成员,以此降低因过度活跃而导致过早触发GC的风险。另外还需警惕诸如装箱/拆箱转换之类的潜在陷阱,它们可能会间接增加额外开销。最后一点就是务必谨慎对待事件处理器注册行为,防止因为遗漏解除绑定而造成隐性的泄露隐患[^2]。 ```csharp public class DisposableResource : IDisposable { private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); // Prevent finalizer from running again. } protected virtual void Dispose(bool disposing) { if (!disposed) { if (disposing) { // Free managed resources here... } // Release unmanaged resources here... disposed = true; } } ~DisposableResource() { // Finalizer as backup cleanup mechanism. Dispose(false); } } ``` 上述代码展示了如何正确实现`IDisposable`接口来确保托管及非托管资源都能得到有效处置[^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值