目录
1、屏障机制为什么要扫描栈,栈上的对象不是会在函数结束时被回收吗
2、在当前go的gc方案基础上,只在堆上启用插入写屏障行不行?
3、在当前go的gc方案基础上,只在堆上启用删除写屏障行不行?
4、在当前go的gc方案基础上,只在堆上启用插入写屏障已经能实现GC,为什么还要在堆上启用删除写屏障?
9、golang gc时,首先要将所有栈对象标黑。这时是如何找到所有的栈对象的,有没有不再被引用的栈对象这种情况,不再被引用的栈对象会被标黑吗
10、闭包引用的局部变量逃逸到堆上,那么这个变量如何被GC找到并被标记的
12、如果闭包结构体在堆上,则通过递归扫描堆上的对象找到并标记。堆上的扫描是如何进行的,是从哪个地方开始的
13、假如一个局部指针变量引用了一个堆对象,在这个局部变量所在的函数结束后,这个堆对象是不是可能还存在,只是等待gc回收
一、逃逸分析
(1)逃逸分析在编译阶段由编译器进行,确定变量存储在栈上还是堆上
(2)逃逸分析并不总是能够完全准确地确定一个对象的生命周期和存储位置
(3)如果一个变量的作用域或者生命周期超出函数范围,则可能逃逸到堆上
(4)向函数传递指针,或函数返回一个指针,可能会逃逸(但若该指针没有被使用,则不会逃逸)
(5)闭包内的变量可能逃逸到堆上,变量可能会逃逸(但是若闭包函数不被调用,则不会逃逸)
(6)如果分配一个大对象,可能会发生逃逸
逃逸分析与堆栈分配
Go 的 逃逸分析 是决定一个变量是否可以在栈上分配的过程。如果一个变量(例如 channel)的生命周期超出了它所在的函数的作用域,Go 运行时会将该变量分配到堆上。如果该变量只在函数内部使用,并且没有返回或传递给其他协程(goroutine),它可能会分配在栈上。
可以通过 go run -gcflags=-m
来查看 Go 编译器的逃逸分析,了解哪些变量分配在堆上,哪些分配在栈上。
二、内存回收
栈对象:在函数返回时回收
堆对象:通过GC回收
GC触发时机
(1)定时2min
(2)分配对象时对分配内存达到阈值,阈值通常是根据堆的当前大小和一些动态调整的因素来确定的。(默认GOGC=100,
增涨率为100%,下次GC会在堆内存达到当前的一倍时)
(3)主动触发GC,且上一次GC已结束
三、GC-标记清楚法
过程:
1、stw开启,停止程序运行。通过在程序前方插入安全点来实现。
2、从根对象开始,标记有引用的节点为活跃节点。
3、清楚未标记的节点
4、stw结束
问题:
1、stw期间程序停止,降低了程序效率
2、扫描期间需要扫描整个堆
3、节点删除之后会有内存碎片
方向:
1、并发标记和删除,不使用stw
三、三色标记法
过程
1、首先将所有节点标记为白色、
2、从根对象开始层序遍历,首先将当前对象标记为灰色,遍历完某对象的子对象之后,将父节点标记为黑色。
3、将白色节点删除
问题:
1、某些白色对象可能还有引用,但却被删除。(当一个白色对象被黑色对象引用,然后被灰色对象删除引用。由于后续只会从遍历灰色对象,该对象最后还是白色,会被删除。)
方向:
1、满足:强三色不变式,或,弱三色不变式。
(1)强三色不变式:不存在白色对象被黑色对象引用
(2)弱三色不变式:黑色对象可以引用白色对象,但存在引用该白色对象的灰色祖先,且这个灰色祖先的引用关系需要持续保护
四、插入写屏障
实现:
1、黑色对象引用白色对象时,需将该白色对象标记为灰色
2、满足强三色不变式
3、不在栈上使用插入屏障:
(1)栈上的操作更加频繁且更高效,使用插入屏障会增加复杂度和开销
(2)栈上的对象有明确的生命周期,可以在函数返回时被回收,不需要插入屏障也能被回收。
问题:
1、需要stw二次扫描栈,避免误删栈上的白色对象。(黑栈对象到白栈对象)
五、删除写屏障
实现:
1、白色对象被删除时,标记为灰色
2、满足弱三色不变式???
问题:
1、增加了浮动垃圾,或者说存在回收延迟(没有被黑色对象引用的白色对象,需要等到下一轮被回收)
2、如果创建新的黑到白的引用,这个白的时新创建出来的,原先没有灰色祖先,那么是否还可行???
六、GC-三色标记+混合写屏障
过程:
1、GC开始前,以栈为单位,将所有栈对象置黑(将栈指针引用的堆对象都置为灰色)
2、GC期间栈上新创建的对象直接置黑(将栈指针引用的堆对象都置为灰色)
3、GC期间,从堆的根对象开始扫描标记
4、堆对象启用插入写屏障、删除写屏障
七、问题
1、屏障机制为什么要扫描栈,栈上的对象不是会在函数结束时被回收吗
(1)栈指针可能引用堆对象,扫描栈的目的是将这部分堆对象置灰,否则这部分堆对象会成为孤立的白色对象,最后被误删。
2、在当前go的gc方案基础上,只在堆上启用插入写屏障行不行?
go三色标记+插入写屏障实现,仅在堆上启用插入写屏障,而不启用删除写屏障:
(1)GC开始前,以栈为单位,将所有栈对象置黑(将栈指针引用的堆对象都置为灰色)
(2)GC期间栈上新创建的对象直接置黑(将栈指针引用的堆对象都置为灰色)
(4)堆对象启用插入写屏障
可行。
在三色标记算法中,三色不变性是最关键的要求,即:黑色对象不应该指向白色对象,否则可能导致未标记的可达对象被误回收。方案中:
- 栈对象直接标黑:在GC开始时和GC期间新创建的栈对象直接标黑,避免了栈上引用的对象被错误回收。
- 堆对象插入写屏障:通过插入写屏障,黑色堆对象不会直接指向白色对象,而是会将新引用的对象置为灰色,从而保持三色不变性。
- 删除写屏障的省略:因为栈对象都立即置黑,且堆上对象通过插入写屏障保持引用完整性,所以删除写屏障不是必需的。栈对象消失时,其所引用的堆对象不会变为不可达,因为堆对象的引用关系都由插入写屏障来维护。
该方案是可行的,因为:
- 栈对象直接置黑避免了栈上对象引用的变化导致的误回收问题。
- 插入写屏障在堆对象中保证了黑色对象不指向白色对象的三色不变性。
- 删除写屏障不是必需的,因为栈对象直接置黑,不会再影响GC的可达性判断
3、在当前go的gc方案基础上,只在堆上启用删除写屏障行不行?
go三色标记+插入写屏障实现,仅在