用户程序通过内存分配器(Allocator)在堆上申请内存,而垃圾收集器(Collector)负责回收堆上的内存空间,内存分配器和垃圾收集器独特管理程序中的堆内存空间。
基本概念
垃圾分类
- 语义垃圾:也就是内存透露,指的是从语法上可达的对象,也就是被其余对象援用的,然而从语义上来讲是垃圾。这类垃圾,垃圾回收是不论的
- 语法垃圾:从语法上是不可达的对象,也就是没有对象援用了,这些垃圾是垃圾回收重点关照的对象
垃圾回收算法
常见的垃圾回收算法:
-
援用计数:
某个对象的根援用计数变为0时,其所有节点均需被回收
-
标记压缩:
将存活对象挪动到一起,无效解决内存碎片问题
-
复制算法:
将所有正在应用的对象从From空间复制到To空间,堆利用率只有一半,也能解决内存碎片问题
-
标记革除:
标记垃圾对象,而后再革除。解决不了内存碎片问题,须要与能尽量避免内存碎片的内存分配器应用,如tcmalloc
标记革除
标记革除算法是最常见的垃圾收集算法,标记革除收集器是跟踪式垃圾收集器,其执行过程能够分为标记、革除两个阶段:
- 标记阶段:从根对象登程查找并标记堆中所有存活的对象
- 革除阶段:遍历堆中的全副对象,回收未被标记的垃圾对象并将回收的内存退出闲暇链表
传统的标记革除算法,垃圾收集器从垃圾收集的根对象登程,递归遍历这些对象指向的子对象并将所有可达的对象标记成存活。标记完结后,垃圾收集器会顺次遍历堆中的对象并革除其中的垃圾,整个过程须要标记对象的存活状态,所以用户程序在垃圾收集的过程中不能执行,也就是常说的STW(Stop The World)。
三色形象
为了解决原始标记革除算法带来的长时间STW,大部分追踪式垃圾收集器都会实现三色标记算法的变种以缩短STW的工夫
三色标记算法将程序中的对象分成红色、彩色、灰色:
- 红色对象:潜在的垃圾,也就是没有被扫到的对象,其内存可能会被垃圾收集器回收
- 彩色对象:沉闷的对象,包含不存在任何援用内部指针的对象以及从根对象可达的对象
- 灰色对象:沉闷的对象,因为存在指向红色对象的内部指针,垃圾收集器会扫描这些对象的子对象
标记过程:
垃圾收集器开始工作的时候,不存在任何彩色对象,根对象会被标记成灰色,垃圾收集器只会从灰色对象汇合中取出对象开始扫描,当灰色汇合中不存在任何对象时,标记就会完结。
大抵工作原理:
- 从灰色对象的汇合中抉择一个灰色对象并将其标记成彩色
- 将彩色对象指向的所有对象都标记成灰色,保障该对象和被该对象援用的对象都不会被回收
- 反复上述两个步骤直到不存在灰色对象
当三色标记完结后,应用程序的堆中就不存在任何灰色对象,咱们只能看到彩色的存活对象以及红色的垃圾对象,垃圾收集器能够回收这些红色的垃圾。
缺点:
因为用户程序可能在标记执行的过程中批改对象的指针,所以三色标记革除算法自身是不能够并发或者增量执行的,仍须要STW。如果原本不应该被回收的对象被回收了,这在内存治理中是十分重大的谬误,这种谬误称为悬挂指针,也就是指针没有指向特定类型的非法对象,影响了内存的安全性,想要并发或者增量标记对象就须要应用屏障技术。
屏障技术
内存屏障技术是一种屏障指令,能够让CPU或编译器在执行内存相干的操作时遵循特定的束缚,目前少数的古代处理器都会乱序执行指令以最大化性能,然而该技术可能保障内存操作的程序性,在内存屏障前执行的操作肯定会先于内存屏障后执行的操作。想在并发或增量的标记算法中保障正确性,须要达到两种三色不变性的其中一种。
三色不变性:
- 强三色不变性:彩色对象不会指向红色对象,只会指向灰色对象或者彩色对象
- 弱三色不变性:彩色对象指向的红色对象必须蕴含一条从灰色对象经由多个红色对象的可达门路
屏障技术分为读屏障和写屏障,然而因为读屏障须要在读操作中退出代码片段,所以对用户程序的性能影响较大。解析一下go语言中应用的两种写屏障技术,插入写屏障和删除写屏障。
插入写屏障(DIJKSTRA)
writePointer(slot, ptr):
shade(ptr)
*slot = ptr
每当要执行*slot = ptr
时,会先执行写屏障通过shade函数尝试扭转指针色彩。如果ptr指针是红色,那么会将该对象设置成灰色。
这张图中能够看到,在一次失常的标记过程中,产生了用户程序批改了指针援用(或者新插入了一个援用关系)的状况,如果咱们采纳 插入写屏障 咱们就须要将新指向的对象标为灰色,以此保障强三色不变性。(对于新指向的对象来说,属于被插入一个援用,所以叫插入写屏障)
在插入写屏障中,咱们的标记过程变成(要害第2步):
- 根本标记流程不变,咱们快进到垃圾收集器将根对象指向A对象标记成彩色,并将A对象指向的对象B标记成灰色。
- 这时用户程序批改A对象的指针,将本来A对象指向B对象的指针指向C对象,这个时候就会触发写屏障将C对象标记为灰色,防止被谬误回收。
- 垃圾收集器顺次遍历程序中的其余灰色对象,将他们别离标记成彩色
删除写屏障(YUASA)
writePointer(slot, ptr)
shade(*slot)
*slot = ptr
删除写屏障会在老对象的援用被删除的时候,将红色的老对象涂成灰色,这样就能够保障弱三色不变性,老对象援用的上游对象肯定能够被灰色对象援用。
应用删除写屏障技术的垃圾收集器和用户程序交替运行的场景中的标记过程:
- 垃圾收集器将根对象指向A对象标记成彩色并将A对象指向的对象B标记成灰色
- 如果这时用户程序将B指向C的指针删除,那么C触发删除写屏障,因为C是红色,所以被涂成灰色
- 垃圾收集器顺次遍历程序中的其余灰色对象,将他们别离标记成彩色
第二步触发删除写屏障的着色,因为删除了B指向C的指针,所以C和D别离违反强三色不变性和弱三色不变性,着色后保障了三色不变性,防止悬挂指针。
简略来说就是,在扭转指针指向的时候,原来指向的那个对象是红色的话就要变成灰色,以此保障弱三色不变性。(对于老对象来说,援用关系被解除了,所以叫删除写屏障)
增量和并发
两种策略优化垃圾收集器不会认为回收垃圾导致长时间STW:
- 增量垃圾收集:增量得标记和革除垃圾,升高应用程序暂停的最长工夫
- 并发垃圾手机:利用多核的计算资源,在用户程序执行时并发标记和革除垃圾
因为两种形式都须要垃圾收集器与用户程序交替执行,所以须要配合屏障技术。
增量收集器
增量收集器将本来工夫较长的暂停工夫切分成多个更小的GC工夫片。增量收集器须要配合三色标记法和屏障技术一起应用。将GC过程分段执行,尽管拉长了总的垃圾回收工夫,然而缩小了程序STW的工夫。不过写屏障还是有些开销的。
并发收集器
利用多核优势,将GC过程与用户程序并行执行(大部分状况下),也是要配合屏障技术。
References
- https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-garbage-collector/
- https://spin.atomicobject.com/2014/09/03/visualizing-garbage-collection-algorithms/
发表回复