go垃圾回收
最后更新于
这有帮助吗?
什么时候最适合做垃圾回收?当没有人进行观察时。用一颗摄像头对眼球进行追踪,当对象的眼睛离开屏幕时就执行垃圾回收。 When is the best time to do a GC? When nobody is looking. Using camera to track eye movement when subject looks away do a GC.
-- Richard Hudson
三色标记算法中,会将对象标记为黑、灰、白三种颜色,白色对象将被回收
假设某个灰色对象 A 指向白色对象 B, 而此时赋值器并发的将黑色对象 C 指向(ref3)了白色对象 B, 并将灰色对象 A 对白色对象 B 的引用移除(ref2),则在继续扫描的过程中, 白色对象 B 永远不会被标记为黑色对象了(回收器不会重新扫描黑色对象)。 进而产生被错误回收的对象 B。【C就会指向一个非法地址,野指针】
也就是说,同时满足以下两个条件就可能出现错误回收非垃圾对象的问题:
条件1:某一黑色对象引用白色对象
条件2:对于某个白色对象,所有和它存在可达关系的灰色对象丢失了访问它的可达路径
一种最简单解决三色标记并发问题的方法是停止所有的赋值器线程,保证标记过程不受干扰,即垃圾回收器中常提到的STW, stop the world
方法。
另外一种思路就是使用赋值器屏障技术使得赋值器在进行指针写操作时同步垃圾回收器,保证不破坏弱三色不变性(屏障技术:给代码操作内存的顺序添加一些限制,即在内存屏障前执行的动作必须先于在你内存屏障后执行的动作)
强三色不变性:黑色对象永远不会指向白色对象
弱三色不变性:黑色对象指向的白色对象至少包含一条由灰色对象经过白色对象的可达路径
垃圾回收之前将所有的对象标记为白色
遍历GC Root Set
,将可达对象标记为灰色
遍历灰色对象列表,将可达的对象从白色标记为灰色;将遍历完的灰色对象标记为黑色
继续三色标记直至灰色对象队列为空
最后将栈空间和堆空间的白色垃圾对象进行回收
目的:实现强三色不变性,防止黑色对象指向白色对象。
在垃圾回收器工作过程中,新被添加的对象为白色,但是给堆上黑色对象添加对象,那么被添加的对象由于立即出发插入写屏障会立即被标志为灰色(白色转为灰色),而给栈上黑色对象添加对象则仍然是白色(因为不会触发插入写屏障)
那么对于栈上对象还是可能出现黑色对象指向白色对象,这样不是又可能出现“野指针”情况了吗?所以当三色标记扫描完毕之后,还会启动STW,然后单独对栈上对象进行一次扫描。
为什么不在栈上使用插入写屏障?因为需要保证函数的执行效率,栈空间是有限的而函数地址也是存在于栈上,如果频繁执行插入写屏障就意味着函数栈地址会频繁出入栈,对函数执行效率产生影响。
目的:实现弱三色不变性,防止丢失灰色对象到白色对象的可达路径(就是说允许黑色对象指向白色对象,但是一定存在一条经过灰色对象达到白色对象)。
在垃圾回收器工作过程中,移除对象所引用的的某一对象,那么被移除的对象会被标志为灰色。
这种方式的回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。
插入写屏障短板:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;
删除写屏障短板:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
混合写屏障过程:
GC
开始时将栈上所有对象标记为黑色,无须STW
GC
期间在栈上创建的新对象均标记为黑色
将被删除的下游对象标记为灰色
将被添加的下游对象标记为灰色
注意第四个场景,被一个已经发生逃逸的对象所引用那么被引用的对象也会发生逃逸,也就意味着不会存在堆对象引用栈对象的情况发生(因为该栈变量在编译阶段的逃逸分析时就会逃逸到堆上)
见
这张图是有问题的,原因是堆上对象不可能引用栈上对象,在中我们得知:一个对象被一个已经发生逃逸的对象所引用也会发生逃逸。