
并发标记后有重新标记对并发标记过程用户线程对引用关系产生的变化进行修正,能保证正确标记出要被清理的对象。
那并发清理如何保证是安全的呢?主要从两个方便质疑可能会出现的问题。
1.用户线程产生的引用关系变化不会影响其清除吗?比如黑色对象在并发清理过程中指向了白色对象,但白色对象被清除掉了
2.用户线程产生的新的对象由于没有被标记,会被清理掉吗?
了解这块内容前,先得了解下三色标记。把遍历对象图过程中遇到的对象,按照“是否访问过”这个条件标记成以下三种颜色:
白色:表示对象尚未被垃圾收集器访问过。在可达性开始阶段,所有的对象都是白色的,若在分析结束的阶段,仍然是白色的对象,说明该对象不可达。
黑色:表示对象已经被垃圾收集器访问过,且这个对象的指向其他对象的所有引用都已经扫描过。黑色的对象代表已经扫描过,代表它是安全存活的。黑色对象不可能直接(不经过灰色对象)指向某个白色对象。
灰色:表示对象已经被垃圾收集器访问过,但这个对象上至少存在一个指向其他对象的引用还没有被扫描过。
了解三色标记后,就可以回答上面第一个疑问,并发清理时和用户线程并发执行是如何保证安全清理的?
首先,并发清理时,标记过程(初始标记、并发标记、重新标记)已经结束,此时大家想象一下在老年代的所有对象中,现在只有黑色和白色的对象,然后就是:黑色对象不可能直接(不过经过灰色对象)指向某个白色对象,即标记结束时这些白色对象是不可达的。由于没有灰色对象,黑色对象也无法改变对象引用到白色对象上,那就可以说此时清理白色对象是安全的。
现在回答第二个疑问,并发清理过程中用户线程在老年代产生的新的对象或者新手代对象晋升到老年代的情况,这些对象会不会被误清除掉?
CMS是标记清除算法,老年代堆的内存是不规整的,已使用的内存和空闲的内存相互交错在一起,虚拟机就必须维护一个列表,记录上那些内存块是可用,在分配时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”,与其相对的分配方式是“指针碰撞”,由堆内存是否规整决定使用那种分配方式。
并发清理时,肯定会有一个白色对象集合,根据白色对象集合遍历清除对象所占用的空间。不妨想象一下,开始垃圾回收前,虚拟机创建三个对象集合,白色对象集合、灰色对象集合、黑色对象集合。刚开始时老年代的对象都是白色,则都在白色对象集合中,随着标记过程开始,白色对象开始转移到灰色对象集合中,再由灰色集合转移到黑色对象集合中,最终只留下白色对象集合和黑色对象集合有对象,除了白色对象集合和黑色对象集合使用以外的内存就是空闲列表了。
并发清理时是根据白色对象集合去清理对象,此时用户线程新产生的老年代对象是分配的空闲列表中,不会影响白色对象集合的内容,清除时就保证了对新产生的对象不产生影响,这些新产生的对象将在下次垃圾回收时进行处理。




