1. ParNew垃圾回收器
- 新生代垃圾回收器
- 配置:-XX:+UseParNewGC
- 特点:多线程垃圾回收机制,需要把工作线程全部停掉
- 默认线程数量:cpu核数,可以通过 -XX:ParallelGCThreads 设置线程数
2. 老年代:CMS
- -XX:+UseConcMarkSweepGC
- 标记清理算法
- 通过追踪GC Roots的方法,先将垃圾对象都标记出来,然后一次性把垃圾对象都回收掉
- 会造成很多内存碎片
- 如果停止工作线程,再慢慢的执行标记清理算法,会导致系统卡死时间过长,响应无法处理,所以CMS采取的是垃圾回收线程和工作线程尽量同时执行的模式来处理。
- 4个阶段:
- 初始标记 (方法的局部变量和类的静态变量是GC Roots)
- 停止工作线程,标记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垃圾回收器,停止工作线程再来一遍垃圾回收,更耗时。