GC的实现
实现GC的策略有很多种,其中最常见一种就是 Tracing garbage collection,或者叫 Mark-Sweep,这种算法会通过一个 root Object,遍历这个该对象引用的变量,并且标记,递归这个过程,这样就确定了所有reachable的对象,剩下的对象即视为garbage。
另一种常见的策略还有引用计数(Reference counting),它是通过为每个对象维护一个引用计数,这代表当前对该对象的引用数目,当引用为0,即代表该对象为 Garage。引用技术有如下缺点
循环引用问题
保存计数带来的空间开销
修改引用数目带来的速度开销以及原子性要求
非实时(一个引用的变化可能递归得导致一系列引用修改,内存释放)
有很多算法可以一定程度解决上述问题,顺便一提,C++使用的智能指针即是基于引用计数实现的,COM对象也使用了引用计数来管理。
Unity 中的GC
Unity的脚本后端是基于Mono的实现(当然现在多了个IL2CPP,不过也是类似的GC实现),而Mono使用的GC是所谓的Boehm–Demers–Weiser garbage collector。是Mark-Sweep 的实现,它会在需要进行GC时占用主线程,进行遍历-标记-垃圾回收的过程,然后在归还主线程控制权。这会导致帧数的突然下降,产生卡顿(不过因为该实现是非压缩式的,所以卡顿现象相对较轻,但是对内存利用率进一步下降了,会有内存碎片的问题。。囧)。所以我们需要慎重地处理对象的创建(内存请求),还有释放(使用GC管理内存是没有主动释放内存的接口的,但是我们可以通过消除对某个对象的引用来做到这一点)。此外,Unity的代码分为两部分:托管与非托管,GC影响的只有托管部分的代码使用的堆内存。而且这个托管堆占用的地址空间不会返还给操作系统。。
C# GC 机制
lua GC 机制
Lua中的GC机制,就是垃圾回收(Garbage Collect),是lua中比较重要的一部分。Lua采用垃圾回收机制对所有的lua对象(GCObject)进行管理。Lua虚拟机会定期运行GC,释放掉已经不再被被引用到的lua对象。
标记:每次执行GC时,先以若干根节点开始,逐个把直接或间接和它们相关的节点都做上标记; 清除:当标记完成后,遍历整个对象链表,把被标记为需要删除的节点一一删除即可。
总的来说,gc被分为下面五个阶段:标记、整理、清扫字符串、清扫、收尾,状态值的大小代表了它们的执行顺序,越小越先执行。