垃圾回收过程的一些过程哪些对象是垃圾?我们在进行垃圾回收的时候,首先要判断哪些对象是存活的。常用的方法有以下两种引用计数法可达性分析法Python判断对象存活Java的算法采用引用计数法,而Java采用可达性分析法。“通过GCROOT可达的对象不能回收,不可达的对象可以回收。搜索路径称为引用链。”不可达的对象会被标记两次,通过GCROOT不可达的会被回收。第一个标记。如果需要执行finalize()方法,则该对象会被放入队列中执行finalize(),如果在finalize()方法中成功关联到引用链上的其他对象,则将其从队列中移除可回收对象的集合(“一般不建议你使用finalize方法”),否则会回收“CommonGCROOTsarefollows”虚拟机栈中引用的对象(栈帧中的局部变量表)对象类静态属性在方法区引用的对象方法区常量引用的对象本地方法栈中JNI(Nativemethod)引用的对象》这样看,程序中有很多GCROOT,每次垃圾回收需要分析GCROOTs的引用链,感觉很费时间,可不可以减少每次扫描的GCROOT?”分代和代际参考事实上,目前的虚拟机大多遵循“分代收集”理论进行设计,其实现是基于两代假设以上的对象大多是短暂的,经历过多次垃圾收集过程后存活下来的对象更难灭亡。因此,堆一般分为新生代和老年代。新生代的GC称为MinorGC,老年代的GC称为OldGC。但是生成之后就有问题了。为了找到新生代的存活对象,我们必须遍历老年代,反之亦然。在进行MinorGC时,如果只遍历新生代,那么ABCD就可以确定为存活对象。但是E不会被判断为存活对象,所以会有问题。为了解决这种跨代引用对象,最笨的办法就是遍历老年代中的对象,找到这些跨代引用对象。但是这种方式对性能影响很大。这时候,我们就不得不提到第三个假设“跨代引用与同代引用相比只是少数”。根据这个假设,我们不需要Generationreferences来扫描整个oldgeneration。“为了避免遍历老年代带来的性能开销,垃圾回收器会引入内存集技术。内存集是一个用来记录跨代引用的表。”比如新生代的内存集保存老年代。引用关系所以在进行MinorGC时,只需要将包含跨代引用的内存区域添加到GCROOT中一起扫描即可。卡表我们前面提到垃圾收集器使用内存集来记录跨代引用。其实你可以把内存集理解为一个接口,把卡表理解为一个实现,类比Map和HashMap。卡表的最简单形式可以只是一个字节数组,HotSpot虚拟机也是如此。下面一行代码是HotSpot默认的卡表标记逻辑:CARD_TABLE[thisaddress>>9]=0;可以看出每个元素映射了512字节的内存)当数组元素的值为0时,说明对应的内存地址没有代际引用对象,否则存在(卡表中的这个元素称为dirty)如何更新卡片表?“使卡表元素变脏的过程,HotSpot是通过写barriers实现的”,即当其他生成对象引用当前生成对象时,在引用赋值阶段更新卡表。具体实现方法类似于AOPvoidoop_field_store(oop*field,oopnew_value){//引用字段赋值操作*field=new_value;//post-writebarrier,这里卡表状态更新完成post_write_barrier(field,new_value);三色标记方法执行思路《如何判断一个对象是否可达?》?这就不得不提三色标记法了”白色:遍历开始时所有对象都是白色灰色:被垃圾收集器访问过,但至少有一个引用没有被访问过黑色:被垃圾收集器访问过,并且所有的引用这个对象已经被访问过,是一个安全存活的对象(GCROOT会被标记为黑色),上图就是一个例子,三色标记方法的执行过程如下,首先,对象B和E被GCROOT引用的对象标记为灰色然后将B和E引用的对象A,C,F标记为灰色,此时B和E标记为黑色等等,最后需要回收标记为白色的对象.三色标记方法问题可访问性分析算法根节点的枚举步骤必须在一个能保证一致性的快照中进行分析,因此必须暂停用户线程(StopTheWorld,STW)在各种优化技术的支持下,暂停时间为alr准备很短。在从根节点开始扫描的过程中,不需要STW,但是也会出现一些问题。由于此时垃圾回收线程和用户线程一直在运行,所以引用关系会发生变化。应该回收的对象被标记为不可回收。不应该被回收的对象被标记为应该被回收。第一个案例影响不大,后面的回收才是大事。就是这样。但是第二种情况会导致致命错误。因此,研究表明只有两个条件同时满足。第二种情况插入一个或多个从黑色到白色对象的引用,并删除从灰色到白色对象的所有引用。为了解决这个问题,我们需要破坏两个条件中的任何一个,从而产生“增量更新”和“原始快照”两种解决方案。CMS使用增量更新,G1使用原始快照。“增量更新破坏了第一个条件。”当黑色对象插入指向白色对象的新引用关系时,记录新插入的引用记录。并发扫描结束后,以记录的引用关系中的黑色对象为根,再次扫描。这个可以简单理解为一旦黑色对象新插入了对白色对象的引用,它又会变回灰色对象。“原始快照会破坏第二个条件。”,记录要删除的引用,并发扫描完成后,以记录的引用关系中的灰色对象为根,再次扫描。这也可以简单理解为,无论是否删除引用关系,都会根据刚开始扫描的那一刻对象图的快照进行查找。参考《深入理解Java虚拟机》GarbageCollector图中展示了七个作用于不同世代的收集器。如果两个收集器之间有连接,就意味着它们可以一起使用。在JDK8中,Serial+CMS和ParNew+SerialOld这两种组合被宣告废弃,JDK9中完全取消了对这些组合的支持。并行和并发是并发编程中的专业术语,在谈到垃圾收集器的上下文时,可以理解为“并行”:指的是多个垃圾收集线程并行工作,但此时用户线程还在等待状态“Concurrent”:指用户线程和垃圾回收线程执行SerialCollector“新生代,标记复制算法,单线程。在执行垃圾收集时,必须挂起所有其他工作线程,直到它完成收集。"ParNew收集器"ParNew本质上是Serial收集器的多线程并行版本"ParallelScavenge收集器"新生代,标记复制算法,多线程,主要关注吞吐量”Throughput=runningusercodetime/(runningusercodetime+runninggarbagecollectiontime)SerialOldcollector“Oldgeneration,mark-sortingalgorithm,singlethread,istheoldgenerationversionoftheSerialcollector”有用的下面两个是在JDK5及之前的版本中配合ParallelScavenge收集器作为CMS收集器故障后备方案,并发收集出现ConcurrentModeFailure时使用ParallelOld收集器。多线程的,是oldParallelScavenge收集器的生成版本。”在注重吞吐量或处理器资源稀缺的场合,可以优先考虑ParallelScavenge和ParallelOldcollector的组合。CMS收集器“Oldgeneration,mark-clearalgorithm,multi-threading,主要关注delay”运行过程分为4步initialmark(CMSinitialmark)concurrentmark(CMSconcurrentmark)re-mark(CMSremark)concurrentsweep(CMSconcurrentsweep)initialmark:MarkGCRootscanDirectassociatedobjectsareveryfast(STWwilloccurinthisstep)concurrentmarking:从GCRoots的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但不需要暂停用户线程,可与垃圾回收并发运行重新标记:为了更正对象中标记因继续运行而发生变化的部分的标记记录用户程序并发标记时(“即三色标记法中的增量更新”,STW也会发生在这一步)并发清理:清理删除标记阶段判断的死对象。由于不需要移动幸存的对象,这个阶段也可以与用户线程并发。总结Collector收集对象及算法Collector类型描述适用场景Serial对于新生代,复制算法简单高效,单线程;适用于内存不大的情况。ParNew新生代,复制算法是一个并行的多线程收集器。新一代的volume-priority收集器,复制算法parallelmulti-threadedcollector与ParNew类似,更注重吞吐量。实现吞吐量可控本身就是服务器级多CPU机器上默认的GC方式。主要适用于后台运行,不需要太多多交互任务收集器收集对象和算法收集器类型描述适用场景SerialOld老年代,标记和排序算法单线程Client模式虚拟机使用ParallelOld老年代,标记和排序算法Parallelmulti-threadedcollectorParallerScavengecollector老年代版本是为了配置ParallelSvavenge面向吞吐量的特性而开发的相应组合。CMS用在比较注重吞吐量和CPU资源的场合。用户线程停止时间;缺点是:1.内存碎片,2.需要更多CPU资源,3.浮动垃圾问题,需要更多大堆空间非常重视相应的服务速度、系统停顿时间和用户体验互联网网站或B/S系统互联网后台目前主流垃圾收集器cmsG1横跨新生代和老年代;marksorting+breakingintoparts并行和并发收集器是在JDK1.7中正式引入的。采用分区回收的思想,在不牺牲吞吐量的情况下,以低停顿完成内存回收;可预测的暂停是它最大的优势。本文转载自微信公众号》《Java知堂》,可通过以下二维码关注。转载请联系Java视堂公众号。
