2. ParNew垃圾回收器和CMS垃圾回收器

1. ParNew垃圾回收器

  • 新生代垃圾回收器
  • 配置:-XX:+UseParNewGC
  • 特点:多线程垃圾回收机制,需要把工作线程全部停掉
  • 默认线程数量:cpu核数,可以通过 -XX:ParallelGCThreads 设置线程数

 

2. 老年代:CMS

  • -XX:+UseConcMarkSweepGC
  • 标记清理算法
  • 通过追踪GC Roots的方法,先将垃圾对象都标记出来,然后一次性把垃圾对象都回收掉
  • 会造成很多内存碎片
  • 如果停止工作线程,再慢慢的执行标记清理算法,会导致系统卡死时间过长,响应无法处理,所以CMS采取的是垃圾回收线程和工作线程尽量同时执行的模式来处理。
  • 4个阶段:
    • 初始标记 (方法的局部变量和类的静态变量是GC Roots)
      • 停止工作线程,标记GC roots直接引用的对象,速度快
    • 并发标记
      • 工作线程活动,对老年代所有对象进行GC roots追踪,最耗时,影响小
    • 重新标记
      • 停止工作线程,重新标记第二阶段里新创建的对象,还有一些失去引用的对象,速度快
    • 并发清理
      • 工作线程活动,清理之前被标记的对象,很耗时,影响小
  • 存在问题:
    • 消耗cpu资源:并发标记和并发清理阶段,CMS默认启动的线程数是(cpu核数+3)/4
    • 浮动垃圾:并发清理阶段产生的垃圾。
    • 所以为了保证CMS垃圾回收期间还有空间让对象进入老年代,一般会预留一些空间,-XX:CMSInitiatingOccupancyFaction用来设置老年代占用多少比例时触发CMS,jdk1.6是92%。如果垃圾回收期间要放入老年代的对象大于老年代剩余空间,此时就会自动用“Serial Old”垃圾回收器替代CMS,就是直接强行把系统程序“Stop the World”,重新进行长时间的GC Roots追踪,标记出来全部垃圾对象,不允许新的对象产生然后一次性把垃圾对象都回收掉,完事儿了再恢复系统线程。
    • 内存碎片问题:-XX:+UseCMSCompactAtFullCollection参数默认打开,意思是FullGC后还要停止工作线程,进行碎片整理,-XX:CMSFullGCsBeforeCompaction参数设置多少次FullGc后进行一次内存整理,默认是0
  • 为什么老年代FullGc比新生代MinorGc慢很多?
    • 新生代存活对象少,标记完之后直接把对象放入suvivor就行了,速度快
    • 老年代存活对象多,过程慢,并发清理阶段不是一次回收一大批内存,需要去找零散在各个地方的对象,最后还得执行碎片整理,如果回收期间内存不足以容纳要进入老年代的对象,还得启用Serial Old垃圾回收器,停止工作线程再来一遍垃圾回收,更耗时。

你可能感兴趣的:(java)