JVM垃圾回收篇-垃圾回收器
串行垃圾回收器
Serial
串行:为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有用户的线程,所以不适合服务器环境,适用于堆内存小,适合于个人电脑
开启串行垃圾回收
-XX:+UseSerialGC = Serial + SerialOld
- Serial:开启新生代串行垃圾回收,采用复制算法
- SerialOld:开启老年代串行垃圾回收,采用标记整理算法
- 首先让用户线程到达一个安全点[垃圾回收期间涉及内存地址改变],所有用户线程停止运行,垃圾回收线程对垃圾进行回收,等待垃圾回收完成后,用户线程恢复运行
吞吐量优先的垃圾回收器
-
多线程
-
堆内存较大,需要多核cpu支持(否则就是多个线程争抢同一个cpu的时间片段)
-
单位时间内,stw的时间最短
0.2 0.2=0.4 一小时内总的stw时间最短,单次较长
开启吞吐量优先的垃圾回收器
-XX:+UseParallelGC ~ -XX:+UseParallelOldGC //开启一个,另一个也会自动开启(jdk1.8默认开启)
-
+UseParallelGC:开启新生代吞吐量优先垃圾回收,采用复制算法
-
+UseParallelOldGC:开启老年代吞吐量优先垃圾回收,采用标记整理算法
-
-XX:ParallelGCThreads=n 设置垃圾回收线程数,如果不设置,默认垃圾回收线程数与CPU核心数保持一致
-
-XX:+UseAdaptiveSizePolicy 根据GC的情况自动计算计算 Eden、From 和 To 区的大小,晋升阈值也会受到影响,jdk1.8默认开启
-
-XX:GCTimeRatio=ratio GC时间占比,
ratio
默认为99,计算公式:gc时间占比=1/(1+ratio),即默认占比为1/100,相当于100分种垃圾回收时间不大于1分钟,如果大于1分钟,会自动调整堆内存大小,加大堆内存heap size++ -> gc count--
-
-XX:MaxGCPauseMillis=ms 垃圾回收最大暂停时间
- 默认200ms,当堆内存增大时,每次gc所需时间也会增大(需要扫描堆中的垃圾,扫描和回收时间都会增大),所以需要保障最大暂停时间意味着heap减小,显然,这与
GCTimeRatio
相冲突
- 默认200ms,当堆内存增大时,每次gc所需时间也会增大(需要扫描堆中的垃圾,扫描和回收时间都会增大),所以需要保障最大暂停时间意味着heap减小,显然,这与
响应时间优先的垃圾回收器
-
多线程
-
堆内存较大,多核cpu场景
-
尽可能让单次
stw
的时间最短0.1 0.1 0.1 0.1 0.1 =0.5 单次时间最短 总时间较长
-
开启响应时间优先的垃圾回收器
-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld
- UseConcMarkSweepGC(CMS):基于标记清除的垃圾回收器,并且是并发的,工作在老年代
- UseParNewGC:工作在新生代的垃圾回收器,采用复制算法
- 当
CMS
垃圾回收器并发失败concurrent mode fialure
时,会退化为SerialOld
垃圾回收器 -XX:ParallelGCThreads=n
(垃圾回收并行线程数) ~-XX:ConcGCThreads=threads
(垃圾回收并发线程数),并发线程数一般设置为并行线程数的1/4-XX:CMSInitiatingOccupancyFraction=percent
执行cms垃圾回收时的内存占比,假设percent=80,即老年代内存占用到达80%就触发一次内存清理(由于是并发清理,清理期间会产生新的垃圾[浮动垃圾],需要预留空间给这部分垃圾占用)-XX:+CMSScavengeBeforeRemark
在 CMS GC 的重新标记 阶段开始前先使用ParNewGC
进行一次 Young GC,有利于减少 Young Gen 对 Old Gen 的无效引用,降低 CMS-remark 阶段的时间开销
存在问题
- CMS垃圾回收器采用的是标记清除算法,所以会产生内存碎片
- CMS的GC耗时80%都在remark阶段,remark阶段停顿时间会很长
- concurrent mode failure:这个异常发生在cms正在回收的时候。执行CMS GC的过程中,同时业务线程也在运行,当年轻代空间满了,执行
ygc
时,需要将存活的对象放入到老年代,而此时老年代空间不足,这时CMS还没有机会回收老年代产生的,或者在做Minor GC的时候,新生代救助空间放不下,需要放入老年代,而老年代也放不下而产生的 - promotion failed:在进行Minor GC时,Survivor空间不足,对象只能放入老年代,而此时老年代也放不下造成的,多数是由于老年代有足够的空闲空间,但是由于碎片较多,新生代要转移到老年带的对象比较大,找不到一段连续区域存放这个对象导致的,发生
promotion failed
的下一步就会产生concurrent mode fialure
,将垃圾回收器退化为SerialOld
,此时吞吐量下降严重
CMS垃圾回收器的四个阶段**
-
阶段一(初始标记):标记老年代中所有的根对象,包括根对象直接引用的对象,以及被年轻代中所有存活的对象所引用的老年代对象(只是标记一下GC Roots能直接关联到的对象,速度很快),会触发
stw
-
阶段二(并发标记):从初始标记阶段标记的对象开始找出所有存活的对象
因为是并发运行的,在运行期间会发生新生代的对象晋升到老年代、或者是直接在老年代分配对象、或者更新老年代对象的引用关系等等,对于这些对象,都是需要进行重新标记的,否则有些对象就会被遗漏,发生漏标的情况。为了提高重新标记的效率,该阶段会把上述对象所在的Card标识为Dirty,后续只需扫描这些Dirty Card的对象,避免扫描整个老年代; 并发标记阶段只负责将引用发生改变的Card标记为Dirty状态,不负责处理
-
阶段三(重新标记):为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。此阶段也需要
stw
-
阶段四(并发清除):这个阶段主要是清除那些没有标记的对象并且回收空间)