6.内存回收的具体实现-垃圾收集器


       上图展示了7种用于不同分代的垃圾收集器;

       如果两个收集器之间存在连线,这说明它们可以搭配使用;
      
       虚拟机所处的区域,则表示它是属于新生代收集器还是老年代收集器;

Serial收集器

       具有两个特点:

       1.只会使用一个线程去完成垃圾回收工作;

       2.在进行垃圾回收的时候,必须暂停其他所有的工作线程,直至它收集结束;Stop The World


     

       Serial收集器仍然是Client模式下的默认新生代收集器,它有优于其他收集器的地方:简单而高效(与其他收集器的单线程相比)。

       特别是对于单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。

       虚拟机开发团队一直在为消除或者减少工作线程因内存回收而导致停顿而努力,垃圾收集器的不断发展,用户线程的停顿时间在不断地缩短,但仍然没有办法完全消除。

       在桌面应用场景中,分配给虚拟机管理的内存一般不会很大,收集几十到一两百兆的新生代,停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只有不频繁的发生,这停顿是可以接受的。

ParNew收集器

       ParNew收集器就是Serial的多线程版本,除了使用多条线程去进行垃圾回收之外,其余行为包括Serial收集器可用的控制参数,收集算法,GC停顿,对象分配规则,回收策略等都与Serial一样。

       ParNew是运行在Server模式下的虚拟机首选的新生代收集器,最大的原因是,目前除了Serial收集器外,只有它能与CMS收集器配合工作。

       ParNew收集器在单CPU的环境绝对不会比Serial收集器效果更好,由于存在线程交互开销。它默认开启的收集线程数与CPU的数量相同。

       可以用-XX:ParallelGCThreads参数来限制垃圾收集的线程数量。

Parallel Scavenge(PS)收集器

       新生代的垃圾收集器,使用的是复制算法(当然,商业虚拟机的新生代一般都是复制算法),是并行的多线程收集器。

       它与其他收集器的不同点在于,第一,它是吞吐量优先收集器,例如CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而它的目标则是达到一个可控制的吞吐量。

       吞吐量是指,CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间)。

       停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台计算而不需要太多的交互的任务。

       它提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis和直接设置吞吐量大小的-XX:GCTimeRatio。

      第二,GC自适应的调节策略;设置了-XX:+UseAdaptiveSizePolicy参数后,就不需要手动设置新生代的大小,Eden与Survivor区的大小比例,晋升老年代对象年龄等参数了。它会根据系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量;

------------------------------------------

Serial Old收集器

       Serial收集器的老年代版本;

       单线程收集器,使用"标记-整理"算法;

       Client模式下使用;


Parallel Old(PS Old)收集器

       Parallel Scavenge收集器的老年代版本;

       "标记-整理"算法,多线程;

       在注重吞吐量和CPU资源敏感的场合,可以优先考虑Parallel Scavenge+Parallel Old;


CMS(Concurrent Mark Swep)收集器

       以获得最短停顿时间为目标;

       并发收集

       标记-清除

       分为4个步骤:

       1.初始标记(CMS initial mark):仅仅只标记一下GC Roots能直接关联到的对象,速度很快;

       2.并发标记(CMS concurrent mark):进行GC Roots Tracing的过程;

       3.重新标记(CMS remark):修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记稍微长一点,但远比并发标记的时间短;

       4.并发清除(CMS concurrent sweep)

       初始标记,重新标记,这两个步骤需要“Stop The World”;

       整个过程中,耗时最长的是,并发标记和并发清除,但它们能和用户线程一起工作;所以,从整体上看,CMS收集器的内存回收过程是与用户线程一起并发执行的。


 -------------------------------------------------------       

缺点如下:

        1.对CPU资源敏感;CMS默认启动的回收线程数是(CPU数量+3)/4;关于这里,需要看书详细了解一下;

        2.无法处理浮动垃圾,可能会出现“Concurrent Mode Failure”失败,而导致一次Full GC;

           由于并发清理阶段,用户线程还在运行会有新的垃圾产生;这些垃圾出现在标记过程之后,当次收集无法回收它们,只能下一次GC时再清理;

           这种垃圾就是浮动垃圾;

           由于在垃圾收集阶段,用户线程还需要运行,所以需要预留内存给用户线程使用;因此,CMS不能像其他收集器那样等到老年代几乎被填满了之后再收集;

           在JDK 1.5中,当老年代被使用了68%时,CMS就启动了;JDK1.6中,已经提升至92%;降低垃圾回收次数,提升性能;

           要是,CMS运行期间预留的内存无法满足要求,就出现Concurrent Mode Failure失败,这时虚拟机将启动Serial Old收集器,将导致很长的停顿时间;

       3.CMS是基于标记-清除的,所以,会有空间碎片的问题;Mark.这里还需要补充;

G1(Garbage First)收集器

       面向服务端应用的并行垃圾收集器;Garbage First即垃圾优先,侧重点在于处理垃圾最多的Region;

       具备以下特点:

       1.并行与并发;使用并行缩短Stop-The-World的停顿时间;在垃圾回收时,通过并发让其他线程继续执行;

       2.分代收集:虽然G1可以不需要其他垃圾收集器的配合,就能独立管理整个GC堆;

       3.空间整合:从整体上讲,G1是基于“标记-整理”;从局部(两个Region之间)上来看是基于“复制”算法实现的;

         G1在运行期间是不会产生内存空间碎片的;

       4.可预测的停顿:相对于CMS,是个优势;降低停顿时间是G1和CMS的共同目标,但G1建立了可预测停顿时间的模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾回收上的时间不超过N毫秒;

----------------------------------------------

       其他垃圾收集器是把堆分为新生代,老年代;

       G1是将整个堆分为多个大小相等的独立区域(Region),虽然还有新生代,老年代的概念;但新生代,老年代不再是物理隔离了,它们是一部分Region(不需要连续)的集合;即,每个Region可以属于老年代或者新生代;

       G1之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。

       G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需要的时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。

       这种使用Region划分内存空间以及有优先级的区域回收方式,保证G1收集器在有限的时间内可以获得更高的收集效率;

-----------------------------------------------

      针对不同Region之间,以及其他垃圾收集器中的新生代与老年代之间的对象引用,JVM采用了Remembered Set来避免全堆扫描;

     G1中每个Region都有一个关联的Remembered Set,每当发现对一个Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference对象引用的对象是否在另外一个Region之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中的对象);

      如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remember Set中;

      当进行内存回收时,在GC根节点的枚举范围中加入Remember Set,即可保证不对全堆进行扫描也不会有遗漏;

--------------------------------------------------

      如果,不计算维护Remember Set的操作,G1收集起的运行大致有以下几个步骤:

      1.初始标记:标记一下与GC Roots直接关联的对象,并修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能早正确可用的Region中创建对象;Stop-The-World;

      2.并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象,此阶段耗时较长,但可与用户程序并发执行;

      3.最终标记:此阶段是为了修正在并发标记期间因为用户线程继续运行而导致标记产生变动的那一份标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这段时间需要停顿线程,但是可并行执行;Stop-The-World;可以并行进行标记;

      4.筛选回收:对各个Region的回收价值和成本进行排序,根据用户期望的GC停顿时间来制定回收计划;

        这个阶段其实是可以和其他线程一起并发执行的,但是因为只会说部分Region,时间是可以控制的,而且停顿其他线程将大幅度提高回收效率;

        所以,最终,这个阶段也是,Stop The World;


总结

  • 如果你想要最小化地使用内存和并行开销,选择Serial GC;
  • 如果你想要最大化应用程序的吞吐量,请选Parallel GC;
  • 如果你想要最小化GC的中断或停顿时间,请选CMS GC;

关于G1以后会详细介绍;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值