1、串行
- 单线程
- 堆内存较小的时候,适合个人电脑
2、吞吐量优先
- 多线程
- 堆内存较大,多核CPU
- 我要在单位时间内,占用的stop the world占用的时间最短
- 0.2 + 0.2 ,虽然时间长,但是发生的次数少
3、响应时间优先
- 多线程
- 堆内存较大,多核CPU
- 注重垃圾回收的时候,让单次的stop the world的时间尽可能的短
- 0.1 + 0.1 + 0.1 + 0.1 + 0.1,即发生很多次,但是每次时间都很短
串行垃圾回收器
-XX:UseSerialGC = Serial + SerialOld
Serial 工作在新生代,采用标记复制算法
SerialOld 工作在老年代 采用标记整理算法
吞吐量优先
-XX:+UseParallelGC 新生代垃圾收集器,标记复制算法
-XX:+UsrParallelOldGC 老年代垃圾收集器,标记整理算法
这俩开启一个,默认开启另外一个
开启的垃圾回收线程的个数默认情况下与CPU核数有关,只要CPU核数小于8,就使用全部的CPU来用于垃圾回收,四核CPU我就用4个线程来完成回收,因为利用全部的CPU来完成垃圾回收,因此回收的时候CPU的占用率会达到几乎 100%。
-XX:ParallelGCThreads
—指定并行垃圾收集器执行的线程数量;默认为所在机器的逻辑核数;当逻辑核数超过8时计算公式为:
ParallelGCThreads = 8 + ((N - 8) * 5/8):N为逻辑核数-XX:+UseAdaptiveSizePolicy 采用自适应的方式,动态的调整Eden区和survice区的比例
-XX:GCTimeRatio=ratio 调整垃圾回收时间与总时间的占比 1 / 1 + ratio,ratio默认为99,即0.01,工作了100分钟,只有一分钟可以用于垃圾回收,如果达不到这个目标,GC就会尝试调整堆的大小,比如增大堆内存,减少垃圾回收的次数。
-XX:MaxGCPauseMillis=ms 回收最大暂停毫秒数,默认值200ms,但是事实上和GCTimeRatio是冲突的,因为如果想要减少暂停的毫秒数,就需要减少堆的大小加快回收的速度,而GCTimeRatio想要减少垃圾回收的次数,就要增大堆的内存
响应时间优先
concurrent - Mark - sweep 基于标记清除的并发垃圾收集器
并发
垃圾回收器工作的同时,其他的用户线程也可以执行
并行
多个垃圾回收区并行运行,但不允许用户线程继续运行(stop the world)
-XX:+UseConcMarkSweepGC 老年代
-XX:+UseParNewGC 新生代 标记复制算法
但 CMS垃圾回收器,有时候会发生一个并发失败的问题,此时会让CMS退化成SerialOld回收器
-XX:ParallelGCThreads=n 并行的垃圾回收线程数
-XX:ConcGCThreads=threads 并发的垃圾回收线程,默认为(ParallelGCThreads+3)/4
-CMSInitiatingOccupancyFracion=parcent
由于CMS收集器无法处理“浮动垃圾”,有可能出现“Con-current Mode Failure”失败进而导致另一次完全“Stop The World”的Full GC的产生。在CMS的并发标记和并发清理阶段,用户线程是还在继续运行的,程序在运行自然就还会伴随有新的垃圾对象不断产生,但这一部分垃圾对象是出现在标记过程结束以后,CMS无法在当次收集中处理掉它们,只好留待下一次垃圾收集 时再清理掉。这一部分垃圾就称为“浮动垃圾”。同样也是由于在垃圾收集阶段用户线程还需要持续运行,那就还需要预留足够内存空间提供给用户线程使用,因此CMS收集器不能像其他收集器那样等待到老年代几乎完全被填满了再进行收集,必须预留一部分空间供并发收集时的程序运作使用。JDK之后,默认parcent为92%。要是CMS运行期间预留的内存无法满足程序分配新对象的需要,就会出现一次“并发失败”(Concurrent Mode Failure),这时候虚拟机将不 得不启动后备预案:冻结用户线程的执行,临时启用Serial Old收集器来重新进行老年代的垃圾收集, 但这样停顿时间就很长了。
-XX:+CMSScavengeBeforeRemark
在我们重新标记的阶段,有可能新生代的对象可能会引用老年代的对象,所以重新扫描的时候必须扫描整个堆,但是这样会影响性能,但是新生代中对象比较多,而且很多对象未必会存活多长的时间,如果从新生代找老年代,就算找到了,很大情况下那些对象也会被回收,我们相当于做了很多无用功,这个参数就是,在重新标记之前,重新进行一次MinorGC,来减少一些不必要的标记。
当老年代内存不足,等线程都运行到安全点,暂停下来,会进行初始标记,初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,在期间会暂停用户线程。
初始标记结束之后,用户线程回复运行,GC线程进行并发标记,并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对 象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行。
此后进行重新标记,重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一 些,但也远比并发标记阶段的时间短。重新标记也会暂停用户线程。
CMS采用的是增量更新的重新标记方式,当黑色堆插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过引用关系中的黑色对象为根,重新扫描一次,这可以简化为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了。
黑色的对象的引用是已经被扫描过了,且此次回收中不会被回收,回收器不会再去扫描这个对象的引用,但如果收集器扫描之后,黑色对象又加了一条对白色对象的引用,但是收集器并没有扫描到,这个白色对象就要被清除,因此要记录一下新插入的引用,等并发标记之后再扫描一遍。
最后是并发清除阶段,清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象(标记清除算法),所以这个阶段也是可以与用户线程同时并发的。
CMS收集器对处理器资源非常敏感。
CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有很多剩余空间,但就是无法找到足够大的连续空间来分配当前对象,而不得不提前触发一次Full GC的情况。FullGC会进行空间的压缩,为了解决这个问题, CMS收集器提供了一个-XX:+UseCMS-CompactAtFullCollection开关参数(默认是开启的,此参数从 JDK 9开始废弃),用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程,由于这个内存整理必须移动存活对象,是无法并发的,这样空间碎片问题是解 决了,但停顿时间又会变长,因此虚拟机设计者们还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction(此参数从JDK 9开始废弃),这个参数的作用是要求CMS收集器在执行过若干次(数量由参数值决定)不整理空间的Full GC之后,下一次进入Full GC前会先进行碎片整理(默认值为0,表 示每次进入Full GC时都进行碎片整理)