JVM(Java Virtual Machine)即Java虚拟机,Java代码都是在JVM上运行的,所以了解JVM是成为Java高手的毕竟之路。
本系列内容将对JVM的知识进行介绍,是从头学习JVM知识的笔记。
本系列内容根据自己的学习和理解的基础上,并参考《深入理解Java虚拟机》一书介绍的知识所写。如果有写的不对的地方,请各位多多提点。
如果说收集算法是内存回收的方法论,那么垃圾收集器就是具体实现。
接下来将介绍7种收集器,如下图所示,若两个收集器之间有连线,就说明它们可以搭配使用。注:没有万能的收集器,只能从中选择适合的收集器。
这是一个单线程收集器。意味着它只会使用一个 CPU 或一条收集线程去完成收集工作,并且在进行垃圾回收时必须暂停其它所有的工作线程直到收集结束。它采用的是复制算法。
虽然它需要全部暂停(Stop The World),用户体验感差。但是它简单高效,没有其他线程开销专心收集效率自然高。
可以认为是 Serial 收集器的多线程版本。即用多线程进行垃圾收集外,其他的参数或者策略与Serial 收集器一样。
ParNew 收集器相对 Serial 收集器提高了资源利用率,可以在多个CPU上执行GC,但是每个CPU上的效率都不会超过 Serial 收集器的。除此之外,它还可以配合CMS收集器进行收集。
这是一个新生代收集器,也是使用复制算法实现,同时也是并行的多线程收集器,看似与ParNew 收集器类似。Parallel Scavenge 收集器的目标是达到一个可控制的吞吐量(Throughput),所以也常称为“吞吐量优先”收集器。高吞吐量可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
吞吐量 = 运行用户代码时间 / (运行用户代码时间+垃圾收集时间)
虚拟机会根据当前系统运行情况收集性能监控信息,动态调整对应参数以提供合适的停顿时间或者最大吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)。自适应调节策略也是Parallel Scavenge 收集器与ParNew 收集器的一个重要区别。
Serial Old 收集器是 Serial 收集器的老年代版本,单线程,使用 标记 - 整理算法。
Parallel Old 收集器
Parallel Old 是 Parallel Scavenge 收集器的老年代版本。多线程,使用 标记-整理算法
CMS (Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器。基于 标记-清除 算法实现。
CMS 收集器较之前几种运作更复杂一些。运作步骤如下:
CMS 收集器是第一款真正意义上的并发(Concurrent)收集器。整个过程虽然并发标记和并发清除耗时最长,但是可以和用户线程一起并发工作。
CMS收集器的优点是并发、低停顿,也常被称为并发低停顿收集器(Concurrent Low Pause Collector)。
CMS收集器有以下3个缺点:
复习一下并行与并发的知识,并说明一下并行收集与并发收集。
线程中的并行与并发:
并发并不是真正的“同时进行”,而是CPU把一个时间段划分成几个时间片断(时间区间),然后在其间来回切换,CPU处理得飞快,感觉就像是同时进行。
收集器的并行与并发:
面向服务端的垃圾回收器,使命是替换掉CMS收集器。G1收集器将内存布局分为多个大小相等的独立区域(Region),虽然还保留新生区和老年区,但是它可以整体按Region进行回收。
在G1收集器中,每个Region中使用Remembered Set来避免全堆扫描的。
G1的特点:
运作步骤:
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。
当发生Minor GC事件的时候,有一些需要注意的地方:
《深入理解Java虚拟机》一书中对Major GC/Full GC的定义:
老年区GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随着至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
大家应该注意到,目前,这些术语无论是在 JVM 规范还是在垃圾收集研究论文中都没有正式的定义。但是我们可以根据已知的基础之上做出如下定义:
一般情况下,Major GC 通常是由 Minor GC 触发的,很难将它们完全分离开来,许多现代垃圾收集机制会清理部分老年区空间,所以使用“cleaning”一词只是部分正确。
在发生Minor GC时,虚拟机会检查每次晋升进入老年区的大小是否大于老年区的剩余空间大小,如果大于,则直接触发一次Full GC,否则,就查看是否设 置了-XX:+HandlePromotionFailure(允许担保失败),如果允许,则只会进行MinorGC,此时可以容忍内存分配失败;如果不允许,则仍然进行Full GC(这代表着如果设置-XX:+Handle PromotionFailure,则触发MinorGC就会同时触发Full GC,哪怕老年区还有很多内存,所以,最好不要这样做)。
//待补充
垃圾收集器相关常用参数
参数 | 描述 |
---|---|
UseSerialGC | 虚拟机运行在Client模式下的默认值,打开后使用Serial+Serial Old的组合回收。 |
UseParNewGC | 打开后使用ParNew+Serial Old的组合回收。 |
UseConcMarkSweepGC | 打开后使用ParNew+CMS+Serial Old的组合回收。Serial Old作为CMS出现Concurrent Mode Failure的后备收集器。 |
UseParallelGC | 虚拟机运行在Server模式下的默认值,打开后使用Parallel Scavenge+Serial Old的组合回收。 |
UseParallelOldGC | 打开后使用Parallel Scavenge+Parallel Old的组合回收。 |
SurvivorRatio | 新生区中Eden与Survivor区域容量的比值,默认为8,即Eden:Survivor=8:1 |
PretenureSizeThreshold | 设置这个参数后,大于这个参数的对象直接在老年区分配 |
MaxTenuringThreshold | 晋升到老年区的对象年龄,每经过一次Minor GC新生区对象年龄+1 |
UseAdaptiveSizePolicy | 动态调整Java堆中各个区域的大小以及进入老年区的年龄 |
HandlePromotionFailure | 是否允许分配担保失败,即老年区剩余空间不足以应付新生区所有对象都存活的极端情况 |
ParallelGCThreads | 设置并行GC时进行内存回收的线程数 |
GCTimeRatio | GC时间占总时间的比率,默认值99,即允许1%的GC时间。仅在使用Parallel Scavenge生效 |
MaxGCPauseMillis | 设置GC最大停顿时间。仅在使用Parallel Scavenge生效 |
CMSInitiatingOccupancyFraction | 设置CMS在老年代空间被使用多少后触发Full GC,默认值68%。仅在CMS有效 |
UseCMSCompactAtFullCollection | 设置CMS在完成垃圾收集后是否要进行一次碎片整理(Full GC把空间碎片整理下)。仅在CMS有效 |
CMSFullGCsBeforeCompaction | 设置CMS在进行若干次垃圾收集后是否要进行一次碎片整理(Full GC把空间碎片整理下)。仅在CMS有效 |