本文共 1801 字,大约阅读时间需要 6 分钟。
最近面试的时候面试官会问些GC(Garbage Collection)的问题,除了标记清除比较好说之外,感觉其他的都比较难说清除,真是急煞我也。
想了好久,觉得之所以标记清除之所以比较好理解是因为讲述者的比喻非常形象:“给每个客人发一张卫生纸(别想多了),然后记录下客人的名单,要回收的时候就去问客人需不需要,把需要的标记成1,不需要的标记为0,然后逐个回收不需要的卫生纸”,多清晰!至于客人需不需要,那就是怎么遍历的问题啦,而标记是为了解决陷入询问死循环的问题(比如一个客人B说A需要我就需要,而A也说B需要我就需要,所以有必要标记一下这个人有没有问过,问过了就不问了!)。
首先增量GC是一种GC(废话),它的存在是为了解决标记清除的长停顿问题(就是由于垃圾太多,虚拟机大哥清理了好久都没干完,不能腾出空来干正事)。
它有一个原则:
还有一个策略:
新生的对象不是垃圾
当虚拟机访问一个对象的时候,该对象及其相关的对象不是垃圾。
增量GC名称的由来跟全量GC相对,就是每次只处理一小部分的对象。具体还是分为标记阶段和清除阶段来阐述。
在这个阶段,垃圾回收器识别对象并且打标。
虚拟机关于对象的操作有以下几种:
对于发生这两种操作的对象,垃圾回收器会给他们打一个标,表示:该对象是有用的,不能回收!
同时会有一些事件来触发垃圾回收器的标记行为,比如说生成新对象、改变对象结构,或者是发生函数调用等等,这样垃圾回收器会被唤醒开始干活!打标!
等等,该怎么打标而不会重复而且确保所有对象都访问到呢?垃圾回收器每次醒来的时候程序中的对象都变了呀!不错,所以垃圾回收器要维持一个专门的结构来记录清理的状态,比较直观的想法就是有两个列表,分别是S和U,S用来记录所有的对象,U用来记录待标记的对象。具体的过程如下:
等等!机智的你可能发现了,所有的对象都被标记为有用了!!!这样怎么能找出垃圾呢!!!
没错,这就是下一小节的内容了,千万别离开。
上面给出了一个看似有理、实则好像没谱的过程,不,容我解释!没错,第一次标记过程确实是把所有对象都标记为有用了!但是第一次完成之后可是得继续的哦,我们完成一轮GC之后会把所有对象标记为0,这样好像所有的对象又回到原点了!是的,但是后面有惊喜哦!
我们再按照上面的过程走一遍,先标记root为1,把root的子对象放入U,。。。,漫长的时间过去了,垃圾回收器终于清空了U完成了第二次工作。这时候它发现,有一些对象的标记还是0!!!为什么呢?因为它们不能被访问到啊!!!(垃圾永远是垃圾)所以垃圾回收器终于相信了自己,开始愉快的工作了!
var a = A; // 拦截写操作,标记 Avar b = C; // 标记Cb = B; // 标记B,这是C成为了漂浮垃圾,也就是不被任何对象引用gc(); // 这里会释放C// 这里总共引用了三个对象A,B,C// 可以看出执行gc的时候C已经成为了垃圾对象简化起见,我们假设一次GC进行两轮回收,(正常情况下会进行增量标记,一轮GC过程会被分成很多次执行)