用户程序通过内存分配器(Allocator)在堆上申请内存,而垃圾收集器(Collector)负责回收堆上的内存空间,内存分配器和垃圾收集器共同管理程序中的堆内存空间。
基本概念 垃圾分类语义垃圾:也就是内存泄漏,指的是从语法上可达的对象,也就是被其他对象引用的,但是从语义上来讲是垃圾。这类垃圾,垃圾回收是不管的
语法垃圾:从语法上是不可达的对象,也就是没有对象引用了,这些垃圾是垃圾回收重点照顾的对象
垃圾回收算法常见的垃圾回收算法:
引用计数:
某个对象的根引用计数变为0时,其所有节点均需被回收
标记压缩:
将存活对象移动到一起,有效解决内存碎片问题
复制算法:
将所有正在使用的对象从From空间复制到To空间,堆利用率只有一半,也能解决内存碎片问题
标记清除:
标记垃圾对象,然后再清除。解决不了内存碎片问题,需要与能尽量避免内存碎片的内存分配器使用,如tcmalloc
标记清除标记清除算法是最常见的垃圾收集算法,标记清除收集器是跟踪式垃圾收集器,其执行过程可以分为标记、清除两个阶段:
标记阶段:从根对象出发查找并标记堆中所有存活的对象
清除阶段:遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表
传统的标记清除算法,垃圾收集器从垃圾收集的根对象出发,递归遍历这些对象指向的子对象并将所有可达的对象标记成存活。标记结束后,垃圾收集器会依次遍历堆中的对象并清除其中的垃圾,整个过程需要标记对象的存活状态,所以用户程序在垃圾收集的过程中不能执行,也就是常说的STW(Stop The World)。
为了解决原始标记清除算法带来的长时间STW,大部分追踪式垃圾收集器都会实现三色标记算法的变种以缩短STW的时间
三色标记算法将程序中的对象分成白色、黑色、灰色:
白色对象:潜在的垃圾,也就是没有被扫到的对象,其内存可能会被垃圾收集器回收
黑色对象:活跃的对象,包括不存在任何引用外部指针的对象以及从根对象可达的对象
灰色对象:活跃的对象,因为存在指向白色对象的外部指针,垃圾收集器会扫描这些对象的子对象
标记过程:
垃圾收集器开始工作的时候,不存在任何黑色对象,根对象会被标记成灰色,垃圾收集器只会从灰色对象集合中取出对象开始扫描,当灰色集合中不存在任何对象时,标记就会结束。
大致工作原理:
从灰色对象的集合中选择一个灰色对象并将其标记成黑色
将黑色对象指向的所有对象都标记成灰色,保证该对象和被该对象引用的对象都不会被回收
重复上述两个步骤直到不存在灰色对象
当三色标记结束后,应用程序的堆中就不存在任何灰色对象,我们只能看到黑色的存活对象以及白色的垃圾对象,垃圾收集器可以回收这些白色的垃圾。
缺陷:
由于用户程序可能在标记执行的过程中修改对象的指针,所以三色标记清除算法本身是不可以并发或者增量执行的,仍需要STW。如果本来不应该被回收的对象被回收了,这在内存管理中是非常严重的错误,这种错误称为悬挂指针,也就是指针没有指向特定类型的合法对象,影响了内存的安全性,想要并发或者增量标记对象就需要使用屏障技术。