免规则采集器列表算法(垃圾收集算法的基本运行方式和应用)
优采云 发布时间: 2021-09-18 13:11免规则采集器列表算法(垃圾收集算法的基本运行方式和应用)
垃圾采集算法是一个大话题。首先,应该清楚的是,垃圾采集算法和语言不一定是绑定的。例如,在Java中,不同的JVM实现可能采用不同的算法。其次,垃圾采集算法数量庞大,不可能一一列出。由于篇幅的限制,我们在这里只能做一个非常简单的介绍。如果你想全面了解垃圾采集相关的算法,请参考我的翻译,垃圾采集(豆瓣)
==将线拆分到正文====
根据各种垃圾采集算法最基本的操作模式,可分为三类:
1.参考计数:
基本思想是为每个对象添加一个计数器,以记录对该对象的引用数。每当一个新的参考点指向这个对象时,计数器就会递增一;相反,每次将对此对象的引用设置为null或其他对象时,计数器将递减1。当计数器更改为0时,对象将自动删除
引用计数的优点是1)相对简单,不需要太多运行时支持。它可以用不支持GC的语言实现。当2)对象变成垃圾时,它们将被释放,这不会给正常程序的执行带来额外的中断。它的漏洞是循环引用。对象a收录对对象B的引用,而对象B收录对对象a的引用,并且计数器是盲的。此外,引用计数对正常程序的执行性能有影响(每次引用赋值时都必须更改计数器),特别是在多线程环境中(计数器必须锁定和同步)
仍然主要使用引用计数的示例包括新的C++标准_ptr中苹果的arc和STD::shared
2.标记扫描
基本思想是先按需分配。当没有可用内存时,从寄存器和程序堆栈上的引用开始,遍历由对象作为节点和引用作为边组成的图,标记所有可访问的对象,然后清理内存空间以释放所有未标记的对象
Mark sweep没有问题,它不能处理循环引用,并且在未触发GC时不会影响正常程序的执行性能。但它的问题是,当内存耗尽触发GC时,它需要中断正常程序一段时间来清理内存。当内存中有许多大型对象时,此中断可能很长
有许多使用或部分使用标记清理的示例,但没有一一列出
3.节点复制
基本思想是将整个内存空间分成两部分,可以记录为a和B。所有对象的内存都分配在a中。当a满时,从寄存器和程序堆栈上的引用开始,遍历由对象作为节点和引用作为边组成的图,将所有可访问的对象复制到B,然后交换a和B的角色
与标记扫描相比,节点复制的主要缺点是一半的空间是空闲的,无法使用。另一个模糊的缺点是,它使用内存的方式与现有的内存页更改和缓存进出机制存在潜在冲突。但它有一个很大的优势:所有对象总是紧密地排列在内存中,因此分配内存的任务变得非常简单,只需移动一个指针。对于频繁分配内存的环境,性能优势是相当可观的。此外,由于没有必要清理整个内存空间,如果内存中有少量的活动对象和大量的垃圾对象(某些语言有这种趋势),触发GC引起的中断将小于标记扫描
类似地,也有许多节点复制或部分节点复制的示例,这些示例将不一一列出
==引入基本算法后的分割线====
以上三种基本算法各有优缺点,有许多改进方案。目前,工程实践中最成功的方案应该是分代垃圾采集。它的基本思想是:程序中有大量的临时对象,这些对象在分配后很快就会被释放。同时,如果一个对象在分配后很长一段时间没有回收,很可能它的生命周期很长,尝试采集它是没有用的。因此,我们可以有意识地根据“对象年龄”将记忆划分为几个块,可以记录为老年、中年和青年(XD)。所有分配都是在年轻一代中进行的。年轻一代已经满了,只为年轻一代做GC,然后将幸存的对象移动到中间一代,直到中年和年轻一代都满了,然后将幸存的对象移动到老一代-这只是一个思考的例子,在实践中,分代垃圾采集算法有多种方案,通常同时使用多种基本算法(如绿色生成节点复制、旧代标签清理等)