再啃了一遍python和C#的垃圾回收机制,将其综合对比并总结一下。
Python的垃圾回收机制
首先总结一下python的垃圾回收机制:
1:引用计数。python使用引用计数来统计每个对象被引用的次数,每当python对象引用数量降为0时,其内存会被立刻释放掉。这种方案的优点是原理简单,对象内存能被平稳地释放掉(因为内存释放发生在代码正常执行时,边运行边释放内存)。而缺点是无法解决循环引用问题。因此python的垃圾回收机制除了引用计数外,还补充了其他机制来解决引用计数。
2:标记-清除算法。python运行时会监控内存分配的情况,当达到某些阈值的时候,会触发标记-清除算法遍历所有容器对象,清除其中的循环引用。(参考文章1)
标记-清除算法主要思路是遍历容器对象,根据容器对象对其他对象的引用情况,将真正不可达的对象移到不可达链表(Unreachable List)进行清除。
3:分代回收。上述的标记-清除算法如果不优化的话,那么将会对程序所有的容器对象进行遍历,这种操作太耗了,因此分代回收将容器对象进行分组,减少每次标记-清除时要遍历的对象。(参考文章1)
C#的垃圾回收机制
而C#的垃圾回收机制和python的不同,C#没有使用引用计数方案,而是使用了引用跟踪算法。(参考《CLR via C# 第四版 》21.1.2)
引用跟踪算法构建了无环的对象图,描述了不同对象之间的依赖关系,并通过访问对象图来确定不可达的对象,准确地将不可达的对象内存进行销毁。(对象图概念参考《精通C# 第六版》20.13)
不过和python相比,每个C#引用对象引用数降为0的时候,不会被立刻释放掉,而是在触发垃圾回收的时候才统一销毁。
触发垃圾回收的条件除了主动调用以外,最主要的触发条件是CLR发现第0代的对象内存超出了预算。
这样其实会令游戏项目在垃圾回收时带来比较明显的卡顿,其内存释放不够平稳。
参考
文章1:https://andrewpqc.github.io/2018/10/08/python-memory-management/
书籍:《CLR via C# 第四版》
书籍:《精通C# 第六版》