如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。**Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、版本的虚拟机所提供的垃圾收集器都可能会有很大差别,并且一般都会提供参数供用户根据自己的应用特点和要求组合出各个年代所使用的收集器。
上图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。虚拟机所处的区域,则表示它是属于新生代收集器还是老年代收集器。Hotspot实现了如此多的收集器,正是因为目前并无完美的收集器出现,只是选择对具体应用最适合的收集器。
01、相关概念
01、并行和并发
-
并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
-
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行。而垃圾收集程序运行在另一个CPU上。
02、吞吐量(Throughput)
吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即
吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)。
假设虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
03、Minor GC 和 Full GC
-
新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。具体原理见上一篇文章。
-
老年代GC(Major GC / Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
02、新生代收集器类型
01、Serial收集器(串行,单线程)
下图展示了Serial 收集器(老年代采用Serial Old收集器)的运行过程:
概述
-
收集器是最基本、发展历史最悠久的收集器
-
它是采用 复制算法的新生代收集器
特点
-
它是一个单线程收集器
-
简单、高效
-
适合运行在单核的机器上
-
收集过程全程Stop The World
曾经(JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。,只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停其他所有的工作线程,直至Serial收集器收集结束为止(“Stop The World”)。这项工作是由虚拟机在后台自动发起和自动完成的,在用户不可见的情况下把用户正常工作的线程全部停掉,这对很多应用来说是难以接收的。
场景
客户端应用程序,应用以-client模式运行时,默认使用的就是:Serial
java -client -jar xxxxx.jar
为了消除或减少工作线程因内存回收而导致的停顿,HotSpot虚拟机开发团队在JDK 1.3之后的Java发展历程中研发出了各种其他的优秀收集器,这些将在稍后介绍。但是这些收集器的诞生并不意味着Serial收集器已经“老而无用”,实际上到现在为止,它依然是HotSpot虚拟机运行在Client模式下的默认的新生代收集器。它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程相比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得更高的单线程收集效率。
02、ParNew 收集器(并行,多线程)
概述
ParNew收集器就是Serial收集器的多线程版本,它也是一个新生代收集器。除了使用多线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法(复制算法)、Stop The World、对象分配规则、回收策略等与Serial收集器完全相同,两者共用了相当多的代码。
特点
-
多线程
-
采用复制算法
-
目前只有它能和CMS收集器(Concurrent Mark Sweep)配合工作
-
ParNew 收集器在单CPU的环境中绝对不会有比Serial收集器有更好的效果
-
存在多线程和用户线程的交互的开销争抢CPU的时间的问题。
-
在多CPU环境下,随着CPU的数量增加,它对于GC时系统资源的有效利用是很有好处的。它默认开启的收集线程数与CPU的数量相同,该收集器在通过超线程技术实现的两个CPU的环境中都不能百分之百地保证可以超越
场景
-
目前只有它能和CMS收集器(Concurrent Mark Sweep)配合工作
设置
在CPU非常多的情况下可使用-XX:ParallerGCThreads
设置垃圾收集的线程数量:
-XX:ParallerGCThreads=8
03、Parallel Scavenge 收集器(并行,多线程)
概述
Parallel Scavenge收集器:是一款吞吐量优先的收集器,也是一个并行的多线程新生代收集器,它也使用复制算法。Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标是达到一个可控制的吞吐量(Throughput)。
停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验。而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
特点
-
多线程
-
复制算法
-
也是并行的多线程收集器,这一点和ParNew类似
设置
-
可达到一个可控制的吞吐量
-
-XX:MaxGCPauseMillis=1000 :控制最大的垃圾收集的停顿时间(尽力)单位:毫秒
-
-XX:GCTimeRatio:设置吞吐量的大小,取值是0-100,系统花费不超过1 /(1+n)的时间用于垃圾收集
-
-
GC自适应的调节策略
-
-XX:+UseAdaptiveSizePolicy
这是一个开关参数,打开参数后,就不需要手工指定新生代的大小(-Xmn)、Eden和Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种方式称为GC自适应的调节策略(GC Ergonomics)。自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。
-
场景
注重吞吐量的场景
注意
另外值得注意的一点是,Parallel Scavenge收集器无法与CMS收集器配合使用,所以在JDK 1.6推出Parallel Old之前,如果新生代选择Parallel Scavenge收集器,老年代只有Serial Old收集器能与之配合使用。
03、老年代收集器类型
01、Serial Old收集器(串行,单线程)
概述
-
Serial Old 是年轻代Serial收集器的老年代版本,它同样是一个单线程收集器,
-
使用“标记-整理”(Mark-Compact)算法。
它的工作流程与Serial收集器相同,这里再次给出Serial/Serial Old配合使用的工作流程图:
特点
-
单线程
-
采用的是:标记 + 整理算法
-
会暂停用户线程,造成Stop The World
用途&场景
-
可以和Serial、ParNew、Parallel Scavenge这三个新生代的垃圾收集器配合使用。
-
在JDK1.5 以及之前版本(Parallel Old诞生以前)中与Parallel Scavenge收集器搭配使用。
-
作为CMS收集器的出现故障的时候,用Serial Old作为后备预案。
02、Parallel Old收集器(并行,多线程)
概述
-
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,
-
使用多线程和“标记-整理”算法
-
“吞吐量优先的收集器
特点
-
Parallel Old收集器只能配合Parallel Scavenge收集器使用
-
采用:标记 + 整理算法
-
多线程
场景
-
关注吞吐量的场景
问题
-
它是可以指定停顿时间,如果这个时候停顿时间是1s中,GC在标记和整理,gc线程还在运行,你用户线程1
03、CMS收集器(并发)
概述
-
CMS(Concurrent Mark Sweep) 并发标记清除收集器。
-
并发收集器
-
采用的是:标记+清除算法,从名字上(“Mark Sweep”)就可以看出它是基于“标记-清除”算法实现的。
搭配方式
-
年轻代使用STW式的并行收集
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC
-
老年代回收采用CMS进行垃圾回收
-
对延迟的关注也主要体现在老年代CMS上。
执行过程:
CMS垃圾收集器:一个过程标记-清除 一个过程标记-整理---Serial Old (STW)
CMS收集器;解决Serial Old霸道的stw的问题,同时解决了Parallel Old的错误标记用户线程的问题。
-
CMS收集器工作的整个流程分为以下7个步骤:
-
初始标记(CMS initial mark):
-
-仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,
-
需要“Stop The World”。有STW
-
-
并发标记(CMS concurrent mark):
-
找出所有的Gc Root是能关联到的对象。
-
并发执行,无STW
-
-
并发预清理(Concurrent-preclean):(可能执行也可能不执行)
-
重新标记那些在并发标记阶段,引用被更新的对象(比如:晋升到来年代的对象,或者原生就在老年代的对象),
-
从而减少后面重新标记阶段的工作量。也没有STW。无STW
-
可以通过:-XX:-CMSPrecleaningEnabled关闭并发于预清理阶段。默认是打开的。
-
-
并发可中止的预清理阶段(Concurrent-abortable-preclean):(可能执行也可能不执行)
-
它和并发预清理阶段做的事情是一样的,并发执行并无STW.
-
它执行需要有一个前提:Eden区的使用量大于CMSScheduleRemarkEdenSizeThreshold的阈值(默认是2M)时,才会执行该阶段,否则会跳过.无STW
-
-
重新标记(CMS remark):
-
为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,因为这个阶段可能用户程序还在继续运行,
-
可能会把一些死亡的对象错误的标记成了存活的对象,可能造成部分垃圾对象不能被回收。这种情况还是可以容忍,因为不影响业务
-
可能把一些存活的对象标记成了死亡。这个就问题就大了,程序都办法执行了。
-
-
所以这个阶段主要是去修正并发标记期间,因为用户程序继续运行,导致标记发生变动的那些对象的标记,主要是防止标记有误的一种重新筛选的过程
-
此阶段也需要“Stop The World”。有STW
-
一般来说:重新标记话费的时间会比实际初始标记阶段的时间要长一些。但比并发标记的时间要短一些。(经验之谈)。
-
-
并发清除(CMS concurrent sweep)
-
基于标记结果,清除要清除前面标记出来的垃圾
-
并发执行,无Stop The World 无STW
并发清除:从内存清除的角度思考,会存在内存碎片。
为什么不是并发整理呢?
1:因为在这个阶段清理对象的时候,是并发执行的,如果整理的话要移动对象的位置。
也就说我们很难做到一堆线程在运行用户的业务,一堆线程在垃圾回收,一堆线程在进行整理,回收的过程中还要移动对象,移动对象位置的同时,还要保证程序不出现问题
这个是很难做到的。
-
-
-
并发重置(CMS concurrent reset)
清理本次CMS GC的上下文信息,为下一次GC做准备。
从上面可以看出来,CMS只有在初始标记和重新标记会出现STW。其他阶段都是并发执行的。存在的有点如下:
优点
CMS是一款优秀的收集器,它的主要优点在名字上已经体现出来了:并发收集、低停顿,因此CMS收集器也被称为并发低停顿收集器(Concurrent Low Pause Collector)。
-
STW的时间比较短(初始标记----重新标记)
-
大多数过程都是并发执行
缺点
-
对CPU资源非常敏感 其实,面向并发设计的程序都对CPU资源比较敏感。在并发阶段,它虽然不会导致用户线程停顿,但会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量会降低。CMS默认启动的回收线程数是(CPU数量+3)/4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU不足4个时(比如2个),CMS对用户程序的影响就可能变得很大,如果本来CPU负载就比较大,还要分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了25%,其实也让人无法接受。
-
无法处理浮动垃圾(Floating Garbage) 可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生。*这一部分垃圾出现在标记过程之后,CMS无法再当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就被称为*“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。
-
不能等老年代几乎满了才开始收集 ,这是因为在垃圾收集阶段,用户线程也在运行,要运行就需要申请内存,于是我们必须预留足够的内存,给用户去使用,当预留的内存空间不够->Concurrent Mode Failure - > Serial Old作为后备去收集垃圾对象。
可以使用CMSInitiatingOccupancyFraction设置老年代占比达到多少的时候触发垃圾收集器,默认是:68%
-
标记-清除算法导致的空间碎片 CMS是一款基于“标记-清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象。
CMS收集器总结
-
初始标记阶段(STW)
-
并发标记阶段
-
并发预清理阶段
-
并发可中止的预清理阶段
-
重新标记(STW)
-
并发清除
-
并发重置
-
CMS收集器;解决Serial Old霸道的stw的问题,同时解决了Parallel Old的错误标记用户线程的问题。
04、G1收集器
01、概述
-
G1(Garbage-First)收集器是当今收集器技术发展最前沿的成果之一,
-
它是一款面向服务端应用的垃圾收集器
-
它既可以用在新生代也可以用在老年代
HotSpot开发团队赋予它的使命是(在比较长期的)未来可以替换掉JDK 1.5中发布的CMS收集器。与其他GC收集器相比,G1具备如下特点:
-
并行与并发 G1 能充分利用多CPU、多核环境下的硬件优势,使用多个CPU来缩短“Stop The World”停顿时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
-
分代收集 与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同方式去处理新创建的对象和已存活一段时间、熬过多次GC的旧对象来获取更好的收集效果。
-
空间整合 G1从整体来看是基于“标记-整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的。这意味着G1运行期间不会产生内存空间碎片,收集后能提供规整的可用内存。此特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
-
可预测的停顿 这是G1相对CMS的一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了降低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在GC上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了。
02、内部布局
-
在G1收集器进行收集的范围是整个新生代或者老生代。
-
G1在使用时,Java堆的内存布局与其他收集器有很大区别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留新生代和老年代的概念,
-
但新生代和老年代不再是物理隔离的了,而都是一部分Region(不需要连续)的集合。
03、Region
-
通过参数 -XX:G1HeapRegionSize指定Region区的大小
-
取值范围是:1MB~32MB,应为2的N次幂
-
Region的分类
-
Eden
-
Survivor
-
Old
-
Humongous:用于存储大对象,某一个对象的大小超过的Humongous内存大小的一半,就认为是大对象。如果某个对象超级大,就会占用连续的Humongous空间来进行存储。比如指定:-XX:G1HeapRegionSize=16m 如果你一个大对象是18MB, 这个时候就需要占用连续的2个Humongous内存区域。
-
04 、G1收集器的设计思想
-
把内存分块不同大小的(Region)
-
然后跟踪每个Region里面的垃圾堆积的价值大小
-
然后构建出一个优先列表,根据允许的收集时间,优先回收价值高的Region.
-
其实思想是一种:分而治之,化整为零的思想。
05、G1收集器的垃圾收集机制
-
Young GC
-
当所有的Eden Region满的时候。就会触发Young GC
-
Eden里面的对象会转移到Survivor Region里面去
-
原先的Survivor Region中的对象转移到新的Survivor Region中,或者晋升到Old Region
-
空闲的Region会被放入到空闲的列表中,等待下次被使用。
-
-
Mixed GC(核心和巧妙之处)
-
老年代大小占整个堆的百分比达到一定阈值(可用-XX:InitiatingHeapOccupancyPercent指定:默认是45%)就会触发Mixed GC .
-
-
Mixed GC 会回收所有Young Region。同时回收部分Old Region。
06、G1执行过程
-
初始标记(Initial Marking)
-
标记 GcRoots能够直接关联到的对象,和CMS类似
-
存在Stop The World
-
-
并发标记(Concurrent Marking)
-
同CMS并发标记相同类似
-
并发执行 没有Stop The World
-
-
最终标记 (Final Marking)
-
修正在并发期间引起的变动
-
存在Stop The World
-
-
筛选回收阶段(Live Data Counting And Evacuation)
-
会对各个Region的回收价值和成本进行排序
-
根据用户所期望的停顿时间(MaxGCPauseMillis)来指定回收计划,并选择一些Region进行回收
-
回收过程:
-
选择一系列Region构成一个回收集。(就类似创建一个Set集合)
-
把决定回收的Region中存活对象复制到空的Region中
-
然后删除掉需要回收的Region -> 无内存碎片
-
整个过程采用的复制算法,所以不会存在内存碎片的问题
-
存在Stop The World
-
-
-
Full GC
-
复制对象内存不够,或者无法分配足够内存(比如巨型的对象没有足够连续的内存空间进行分配时)会触发FullGC
-
FullGC模式下,会使用Serial Old模式作为备选,也就是说进行标记+整理
-
优化的原则:就是尽量减少FullGC.
如何减少FullGc呢
-
增加预留内存(增大-XX:G1ReservePercent 默认是:10%)
-
更早的回收垃圾(减少:-XX:InitiatingHeapOccupancyPercent,老年代达到该值就会触发MixedGc 默认是:45%)
-
可以增加并发阶段使用的线程数(增大:-XX:ConcGCThreads)
-
07、G1收集器的特点
-
可以作用整个堆
-
可控的停顿时间
-
MaxGCPauseMills=200
-
-
无内存碎片,因为是复制算法回收
08、G1收集器的使用场景
-
占用内存较大的应用 > 6G+
-
替换CMS垃圾收集器
09、G1 Or CMS
-
如果内存 <=6G 建议使用CMS ,如果内存 > 6G .考虑使用G1
10、G1收集器总结
-
G1把内存划分成了若干个Region区域,化整为零去回收,回收的单位不在是年轻代,老年代而是Region。
-
包括了YoungGC回收年轻代,MixedGc(回收年轻代,部分老年代垃圾,最能体现G1思想的也就是Mixed Gc)、FullGC回收老年代
-
Mixed Gc的执行步骤和CMS有类似之处,但也有很多的差异。
-
主要体现在CMS采用的是标记+清除算法,会存在内存碎片问题。
-
Mixed Gc采用的是:复制算法,只要是把存活的对象复制从一个Region复制到另外一个Region。然后把空的Region进行删除即可。所以MixedGC不会存在内存碎片的问题。
-
05、各垃圾收集器总结
收集器 | 串行、并行or并发 | 新生代/老年代 | 算法 | 目标 | 适用场景 |
---|---|---|---|---|---|
Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单CPU环境下的Client模式 |
Serial Old | 串行 | 老年代 | 标记-整理 | 响应速度优先 | 单CPU环境下的Client模式、CMS的后备预案 |
ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境时在Server模式下与CMS配合 |
Parallel Scavenge | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
Parallel Old | 并行 | 老年代 | 标记-整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
CMS | 并发 | 老年代 | 标记-清除 | 响应速度优先 | 集中在互联网站或B/S系统服务端上的Java应用 |
G1 | 并发 | both | 标记-整理+复制算法 | 响应速度优先 | 面向服务端应用,将来替换CMS |
本文通过详细介绍HotSpot虚拟机的7种垃圾收集器回答了上一篇文章开头提出的三个问题中的第三个——“如何回收”,在下一篇文章中,我们将回答最后一个未被解答的问题——“什么时候回收”。
06、JVM垃圾收集器相关参数
收集器 | 参数及默认值 | 备注 |
---|---|---|
Serial | -XX:+UseSerialGC | 虚拟机在Client模式下的默认值,开启后,使用 Serial + Serial Old 的组合 |
ParNew | -XX:+UseParNewGC | 开启后,使用ParNew + Serial Old的组合 |
-XX:ParallelGCThreads=n | 设置垃圾收集器在并行阶段使用的垃圾收集线程数,当逻辑处理器数量小于8时,n的值与逻辑处理器数量相同;如果逻辑处理器数量大于8个,则n的值大约为逻辑处理器数量的5/8,大多数情况下是这样,除了较大的SPARC系统,其中n的值约为逻辑处理器的5/16。 | |
Parallel Scavenge | -XX:+UseParallelGC | 虚拟机在Server模式下的默认值,开启后,使用 Parallel Scavenge + Serial Old的组合 |
-XX:MaxGCPauseMillis=n | 收集器尽可能保证单次内存回收停顿的时间不超过这个值,但是并不保证不超过该值 | |
-XX:GCTimeRatio=n | 设置吞吐量的大小,取值范围0-100,假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集 | |
-XX:+UseAdaptiveSizePolicy | 开启后,无需人工指定新生代的大小(-Xmn)、 Eden和Survisor的比例(-XX:SurvivorRatio)以及晋升老年代对象的年龄(-XX:PretenureSizeThreshold)等参数,收集器会根据当前系统的运行情况自动调整 | |
Serial Old | 无 | Serial Old是Serial的老年代版本,主要用于 Client 模式下的老生代收集,同时也是 CMS 在发生 Concurrent Mode Failure时的后备方案 |
Parallel Old | -XX:+UseParallelOldGC | 开启后,使用Parallel Scavenge + Parallel Old的组合。Parallel Old是Parallel Scavenge的老年代版本,在注重吞吐量和 CPU 资源敏感的场合,可以优先考虑这个组合 |
CMS | -XX:+UseConcMarkSweepGC | 开启后,使用ParNew + CMS的组合;Serial Old收集器将作为CMS收集器出现 Concurrent Mode Failure 失败后的后备收集器使用 |
-XX:CMSInitiatingOccupancyFraction=68 | CMS 收集器在老年代空间被使用多少后触发垃圾收集,默认68% | |
-XX:+UseCMSCompactAtFullCollection | 在完成垃圾收集后是否要进行一次内存碎片整理,默认开启 | |
-XX:CMSFullGCsBeforeCompaction=0 | 在进行若干次Full GC后就进行一次内存碎片整理,默认0 | |
-XX:+UseCMSInitiatingOccupancyOnly | 允许使用占用值作为启动CMS收集器的唯一标准,一般和CMSFullGCsBeforeCompaction配合使用。如果开启,那么当CMSFullGCsBeforeCompaction达到阈值就开始GC,如果关闭,那么JVM仅在第一次使用CMSFullGCsBeforeCompaction的值,后续则自动调整,默认关闭。 | |
-XX:+CMSParallelRemarkEnabled | 重新标记阶段并行执行,使用此参数可降低标记停顿,默认打开(仅适用于ParNewGC) | |
-XX:+CMSScavengeBeforeRemark | 开启或关闭在CMS重新标记阶段之前的清除(YGC)尝试。新生代里一部分对象会作为GC Roots,让CMS在重新标记之前,做一次YGC,而YGC能够回收掉新生代里大多数对象,这样就可以减少GC Roots的开销。因此,打开此开关,可在一定程度上降低CMS重新标记阶段的扫描时间,当然,开启此开关后,YGC也会消耗一些时间。PS. 开启此开关并不保证在标记阶段前一定会进行清除操作,生产环境建议开启,默认关闭。 | |
CMS-Precleaning | -XX:+CMSPrecleaningEnabled | 是否启用并发预清理,默认开启 |
CMS-AbortablePreclean | -XX:CMSScheduleRemark EdenSizeThreshold=2M | 如果伊甸园的内存使用超过该值,才可能进入“并发可中止的预清理”这个阶段 |
CMS-AbortablePreclean | -XX:CMSMaxAbortablePrecleanLoops=0 | “并发可终止的预清理阶段”的循环次数,默认0,表示不做限制 |
CMS-AbortablePreclean | -XX:+CMSMaxAbortablePrecleanTime=5000 | “并发可终止的预清理”阶段持续的最大时间 |
-XX:+CMSClassUnloadingEnabled | 使用CMS时,是否启用类卸载,默认开启 | |
-XX:+ExplicitGCInvokesConcurrent | 显示调用System.gc()会触发Full GC,会有Stop The World,开启此参数后,可让System.gc()触发的垃圾回收变成一次普通的CMS GC。 | |
-XX:+UseG1GC | 使用G1收集器 | |
-XX:G1HeapRegionSize=n | 设置每个region的大小,该值为2的幂,范围为1MB到32MB,如不指定G1会根据堆的大小自动决定 | |
-XX:MaxGCPauseMillis=200 | 设置最大停顿时间,默认值为200毫秒。 | |
-XX:G1NewSizePercent=5 | 设置年轻代占整个堆的最小百分比,默认值是5,这是个实验参数。需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数。 | |
-XX:G1MaxNewSizePercent=60 | 设置年轻代占整个堆的最大百分比,默认值是60,这是个实验参数。需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数。 | |
-XX:ParallelGCThreads=n | 设置垃圾收集器在并行阶段使用的垃圾收集线程数,当逻辑处理器数量小于8时,n的值与逻辑处理器数量相同;如果逻辑处理器数量大于8个,则n的值大约为逻辑处理器数量的5/8,大多数情况下是这样,除了较大的SPARC系统,其中n的值约为逻辑处理器的5/16。 | |
-XX:ConcGCThreads=n | 设置垃圾收集器并发阶段使用的线程数量,设置n大约为ParallelGCThreads的1/4。 | |
-XX:InitiatingHeapOccupancyPercent=45 | 老年代大小达到该阈值,就触发Mixed GC,默认值为45。 | |
-XX:G1MixedGCLiveThresholdPercent=85 | Region中的对象,活跃度低于该阈值,才可能被包含在Mixed GC收集周期中,默认值为85,这是个实验参数。需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数。 | |
-XX:G1HeapWastePercent=5 | 设置浪费的堆内存百分比,当可回收百分比小于浪费百分比时,JVM就不会启动Mixed GC,从而避免昂贵的GC开销。此参数相当于用来设置允许垃圾对象占用内存的最大百分比。 | |
-XX:G1MixedGCCountTarget=8 | 设置在标记周期完成之后,最多执行多少次Mixed GC,默认值为8。 | |
-XX:G1OldCSetRegionThresholdPercent=10 | 设置在一次Mixed GC中被收集的老年代的比例上限,默认值是Java堆的10%,这是个实验参数。需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数。 | |
-XX:G1ReservePercent=10 | 设置预留空闲内存百分比,虚拟机会保证Java堆有这么多空间可用,从而防止对象晋升时无空间可用而失败,默认值为Java堆的10%。 | |
-XX:-G1PrintHeapRegions | 输出Region被分配和回收的信息,默认false | |
-XX:-G1PrintRegionLivenessInfo | 在清理阶段的并发标记环节,输出堆中的所有Regions的活跃度信息,默认false | |
Shenandoah | -XX:+UseShenandoahGC | 使用UseShenandoahGC,这是个实验参数,需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数;另外该参数只能在Open JDK中使用,Oracle JDK无法使用 |
ZGC | -XX:+UseZGC | 使用ZGC,这是个实验参数,需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数; |
Epsilon | -XX:+UseEpsilonGC | 使用EpsilonGC,这是个实验参数,需用-XX:+UnlockExperimentalVMOptions解锁试验参数后,才能使用该参数; |
07、其他的收集器
01、Shenandoah
02、ZGC
03、Epsilon
08、频繁Full Gc怎么排查?
通常的处理,我们还是要在FullGc时取到当时的dump文件,来分析内存里都有哪些数据占居着内存。这里有两种办法来获取dump文件:
1.通过在jvm里添加参数配置:+HeapDumpBeforeFullGC,+HeapDumpAfterFullGC 这种方法需要在应用启动前要提前配置好,如果不需要的话,还需要修改jvm参数重启应用。
2.使用jinfo命令进行设置。(生产环境常用的方法) 无需重启jvm,即时生效,dump文件生成后,清除JVM参数,通常fullgc 会频繁发生,不需要一直导出dump,所以拿到一次的dump采样后, 即可清除,然后慢慢分析dump文件
01、第一步,通过jps获得java程序的pid(jps,ps等方法)
#jps
5940 Main
3012 Jps
02、第二步,调用jinfo命令设置JVM参数
#jinfo -flag +HeapDumpBeforeFullGC 5940
#jinfo -flag +HeapDumpAfterFullGC 5940
使用 #jinfo -flags pid 检查有没有生效
09、FullGC频繁的排查&解决方案
造成Full GC的原因:
1、程序执行了System.gc() //建议jvm执行fullgc,并不一定会执行 2、执行了jmap -histo:live pid命令 //这个会立即触发fullgc 3、在执行minor gc的时候进行的一系列检查
-
执行Minor GC的时候,JVM会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小。
-
old > young 如果大于,则直接执行Minor GC(这个时候执行是没有风险的)。
-
old < young 如果小于了,JVM会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC。
-
如果开启了,则JVM会检查老年代中最大连续可用空间是否大于了历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC。如果大于则会执行Minor GC,如果Minor GC执行失败则会执行Full GC