垃圾回收机制的分类
CMS
CMS堆内存和以网的垃圾收集器一样,分为新生代和老年代,新生代和老年代是物理隔离的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMEGAHNR-1592646953798)(https://cdn.nlark.com/yuque/0/2020/webp/99448/1592532635546-b62d959d-47cf-4505-b51d-0cd1b66759d3.webp)]
G1
G1将JAVA的堆空间分割成了若干空间大小的区域,region包括Eden区、Survivor、 Old、 Humongous 四种类型。其中Humongous是一个比较特殊的Old类型,专门放置大类型的对象。这样的划分方式意味着不需要一个连续的内存空间管理对象。G1将空间分为多个区域,优先回收垃圾最多的区域,G1采用的是好的空间中和能力不会乘胜大量的空间碎片。
Region的数值是在1M到32M字节之间的一个2的幂值数,JVM会尽量划分2048个左右、同等大小的Region。
GI 的一大优势在于可预测的停顿时间, 能够尽可能快地在指定时间内完成垃圾回收任务。在 JDKl l 中,已经将 GI 设为默认 垃圾回收器。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bmr0UGJ6-1592646953805)(https://cdn.nlark.com/yuque/0/2020/webp/99448/1592533642361-05c937af-c67e-4136-8797-f082a55ac8df.webp)]
ZGC
ZGC和G1类似,但是ZGC的regin的大小更加灵活和动态,zgc的region不会和G1那样在一开始就被划分为固定的regin大小。
zgc的regin核心亮点就是:动态
动态的表现为:
1、动态地创建和销毁
2、动态的决定regin的大小,他的最小单位是2MB的一个块,每一个大小就是2MB*N的大小
切还有一个概念是size groups有三种:
Small:就是一个2MB的regin
Medium:32MB。2MB*16
Large:N*2MB
整个的样子为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FdHh1kr1-1592646953812)(https://cdn.nlark.com/yuque/0/2020/webp/99448/1592533646841-4e658b11-2302-4b01-89cd-404701861fb2.webp)]
JVM内存分配、回收
对象优先在Eden区分配
大多数情况下,对象在新生代中的Eden区分配,当Eden中没有足够的空间进行分配的时候,虚拟机讲发起一次Minor GC。
Minor GC和Full GC有什么区别
新生代GC(Minor GC):指发生新生代的垃圾收集动作,Minor GC非常频繁,回收速度一般比较快。
老年代GC(Major GC/Full GC):指发生在老年代的GC,出现Major GC经常会伴随至少一次的Minor CG(并非绝对),Major GC的速度一般会比Minor GC慢10背以上。
/**
* @author yangxinlei
* @date 2020/6/19
*/
public class GCTest {
public static void main(String[] args) {
byte[] allocation1, allocation2;
allocation1 = new byte[1024*1020*5];
// allocation2 = new byte[1024*1020*5];
}
}
在启动项中增加VM参数为:-XX:+PrintGCDetails
Heap
PSYoungGen total 76288K, used 13352K [0x000000076ae00000, 0x0000000770300000, 0x00000007c0000000) //新生代
eden space 65536K, 100% used [0x000000076ae00000,0x000000076bb0a108,0x000000076ee00000)
from space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000)
to space 10752K, 0% used [0x000000076ee00000,0x000000076ee00000,0x000000076f880000)
ParOldGen total 175104K, used 0K [0x00000006c0a00000, 0x00000006cb500000, 0x000000076ae00000) // 老年代
object space 175104K, 0% used [0x00000006c0a00000,0x00000006c0a00000,0x00000006cb500000)
Metaspace used 3512K, capacity 4498K, committed 4864K, reserved 1056768K // 原空间
class space used 387K, capacity 390K, committed 512K, reserved 1048576K
从上面的数据中可以看到eden中的内存几乎被分配完全,虽然程序什么也没做只是定义出来也用了一部分的空间,我们可以想想到我们再吧allocation2定义出来然后会出现什么情况。
Heap
PSYoungGen total 76288K, used 8252K [0x000000076ae00000, 0x0000000770300000, 0x00000007c0000000)
eden space 65536K, 12% used [0x000000076ae00000,0x000000076b60f0f8,0x000000076ee00000)
from space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000)
to space 10752K, 0% used [0x000000076ee00000,0x000000076ee00000,0x000000076f880000)
ParOldGen total 175104K, used 0K [0x00000006c0a00000, 0x00000006cb500000, 0x000000076ae00000)
object space 175104K, 0% used [0x00000006c0a00000,0x00000006c0a00000,0x00000006cb500000)
Metaspace used 3512K, capacity 4498K, committed 4864K, reserved 1056768K
class space used 387K, capacity 390K, committed 512K, reserved 1048576K
为什么会出现这样的情况 因为当给2分配内存的时候eden区的内存发现内存已经基本没有了, 没有内存的话,虚拟机将会发生一次GC,GC期间的时候虚拟机发现1无法存入Survior空间,所以只好通过 分配担保机制 吧新生成的对象提前转移的老年代中,老年代的内存空间足够方1,所以不会出现Full GC,执行Minor GC后,后面分配的对象如果能够存eden区的话,还是会在eden区分配内存。
大对象会直接进入老年代
大对象就是需要大量的连续的内存空间对象(比如:字符穿、数组)
为什么会这样的?
为了避免大对象分配内存时由于分担机制带来的复制而降低效率
长期存活的对象将进入老年代
既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时候就必须能识别哪些对象应方在新生代,哪些应方在老年代中,为了做到这一点,虚拟机给每个对象一个年纪计数器。
如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为1.对象在 Survivor 中每熬过一次 MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。
怎么对象可以被回收
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
引用计数法
对象中添加一个引用计数器,每当一个地方引用他,计数器就会加1,当引用失效,计数器就减1,任何时候计数器为0的对象就是不可能再被使用的。这个方法实现简单,效率高,但是目前主流的虚拟中并没有选择这个方法来管理内存,主要原因是因为对象之间会相互调用的问题导致计数器不可能为0,也就是最后结果导致无法GC回收器回收他们。
public class Reference {
Object instance = null;
public static void main(String[] args) {
Reference a = new Reference();
Reference b = new Reference();
a.instance = b;
b.instance = a;
a = null;
b = null;
}
}
可达性分析算法
这个算法的基本思想就是通过一列被称为“GC Roots”的对象作为起点,从这些点开始向下搜索,节点所走过的路径被称为引用链,当一个对象到GC ROOTS没有任何引用链相关联的话,则证明此对象是不可用的。
GC Roots根节点:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量等等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eewblJtQ-1592646953834)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646048626-99e79f55-1ccf-4f60-a989-69968e06245b.png)]
finalize()方法最终判定对象是否存活
即使在可达性分析算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历再次标记过程。
标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链。
1、 第一次标记并进行一次筛选
筛选的条件是此对象是否有必要执行finalize()方法。
当对象没有覆盖finalize方法,或者finzlize方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”,对象被回收。
2. 第二次标记
如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为:F-Queue的队列之中,并在稍后由一条虚拟机自动建立的、低优先级的Finalizer线程去执行。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束。这样做的原因是,如果一个对象finalize()方法中执行缓慢,或者发生死循环(更极端的情况),将很可能会导致F-Queue队列中的其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。
finalize()方法是对象脱逃死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象要在finalize()中成功拯救自己----只要重新与引用链上的任何的一个对象建立关联即可,譬如把自己赋值给某个类变量或对象的成员变量,那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱,那基本上它就真的被回收了。
如何判断一个常量是废弃常量
假如在常量池中存在字符串 “ancdef”,如果当前没有任何String对象引用该字符串常量的话,就说明常量 “ancdef” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“ancdef” 就会被系统清理出常量池。
如何判断一个类是无用的类
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是 “无用的类” :
该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
加载该类的 ClassLoader 已经被回收。
该类对应的 java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。
垃圾收集算法
标记--清楚算法
复制算法
标记--整理算法
分代收集算法
标记–清楚算法
算法分为"标记"和"清除",先吧所有需要回收的标记出来,然后在标记完成后统一回收对象,他是最基础的收集算法,销量还是比较高的但是也会有两个比较明显的问题:
效率问题
空间问题: 标记后清理产生大量不连续的碎片
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nDHcI4AU-1592646953838)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646077835-d4b108ed-57e0-454e-b636-8208173d4c6a.png)]
复制算法
为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HtnnkKEK-1592646953845)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646083647-a3b2d7c3-906a-4e3f-97d2-c80deba61008.png)]
标记-整理算法
根据老年代的特点特出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一段移动,然后直接清理掉端边界以外的内存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6o7S5zZv-1592646953848)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646088694-ee2d0444-577e-4ada-b7c5-39b7fb43cacd.png)]
分代收集算法
这个方式是现在比较常见的方式很多垃圾收集都是采用这种算法。
根据对象存活周期的不同将内存分为几块,一般情况下将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
在新生代中,每次收集都会有大量对象死去,可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。
而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
垃圾收集器
serial收集器 串行
parNew收集器 并行
parallel Scavenge收集器 并发
CMS收集器 并发
G1收集器
如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。
每一种垃圾手机机制都是有各自的优点缺点,我们在使用的时候需要根据具体的业务场景选择自己适合的垃圾收集器
serial收集器
Serial(串行)收集器收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZZeeT9CM-1592646953854)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646100145-2fef8015-0c9b-433e-bcd1-267a8d76d28d.png)]
虚拟机的设计者们当然知道Stop The World带来的不良用户体验,所以在后续的垃圾收集器设计中停顿时间在不断缩短(仍然还有停顿,寻找最优秀的垃圾收集器的过程仍然在继续)。
虽然他单线程导致停顿,但是他效率还是比较高相对于其他单线程收集器来说
ParNew收集器
其实是serial收集器多线程版,除了使用多个线程进行垃圾分类外,其余行为控制参数,收集算法,回收策略等等,和serial收集器完全一样
新生代采用复制算法,老年代采用标记-整理算法。
它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器(真正意义上的并发收集器,后面会介绍到)配合工作。
并行和并发:
并行(Parallel) : 指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。适合科学计算、后台处理等弱交互场景。
并发(Concurrent): 指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个CPU上。适合Web应用。
Parallel Scavenge收集器
似于ParNew 收集器,是Server 模式(内存大于2G,2个cpu)下的默认收集器,Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。
Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHdBF7S2-1592646953856)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646107724-5ebecfc6-f24f-4b7f-ab21-0ecce801ad01.png)]
新生代采用复制算法,老年代采用标记-整理算法。
Serial Old收集器
Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用,另一种用途是作为CMS收集器的后备方案。
Parallel Old收集器
Parallel Scavenge收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器。
CMS收集器(-XX:+UseConcMarkSweepGC(主要是old区使用))
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它而非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
从名字中的Mark Sweep这两个词可以看出,CMS收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:
初始标记: 暂停所有的其他线程(STW),并记录下直接与root相连的对象,速度很快 ;
并发标记: 同时开启GC和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以GC线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。
重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
并发清除: 开启用户线程,同时GC线程开始对未标记的区域做清扫。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OdhjxsNc-1592646953859)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646125304-e17f9bfa-4f96-4f36-8862-c459cf71e679.png)]
从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面三个明显的缺点:
对CPU资源敏感(会和服务抢资源);
无法处理浮动垃圾(在java业务程序线程与垃圾收集线程并发执行过程中又产生的垃圾,这种浮动垃圾只能等到下一次gc再清理了);
它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。
CMS的相关参数
-XX:+UseConcMarkSweepGC 启用cms
-XX:ConcGCThreads:并发的GC线程数(并非STW时间,而是和服务一起执行的线程数)
-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩(减少碎片)
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次(因压缩非常的消耗时间,所以不能每次FullGC都做)
-XX:CMSInitiatingOccupancyFraction:触发FulGC条件(默认是92)
-XX:+UseCMSInitiatingOccupancyOnly:是否动态调节
-XX:+CMSScavengeBeforeRemark:FullGC之前先做YGC(一般这个参数是打开的)
-XX:+CMSClassUnloadingEnabled:启用回收Perm区(jdk1.7及以前)
G1收集器(-XX:+UseG1GC)
G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nZLYGXUh-1592646953860)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646142759-7d97efd4-485f-41e6-8147-9aac860c7544.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PudLPSvE-1592646953863)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646147086-b7ccdb87-fb20-4315-97b8-70341d669828.png)]
G1将Java堆划分为多个大小相等的独立区域(Region),虽保留新生代和老年代的概念,但不再是物理隔阂了,它们都是(可以不连续)Region的集合。
分配大对象(直接进Humongous区,专门存放短期巨型对象,不用直接进老年代,避免Full GC的大量开销)不会因为无法找到连续空间而提前触发下一次GC。
被视为JDK1.7中HotSpot虚拟机的一个重要进化特征。它具备以下特点:
并行与并发: G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程来执行GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
分代收集: 虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。
空间整合: 与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。
可预测的停顿: 这是G1相对于CMS的另一个大优势,降低停顿时间是G1 和 CMS 共同的关注点,但G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内完成垃圾收集。
G1收集器的运作大致分为以下几个步骤:
**初始标记(initial mark,STW):**在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。
并发标记(Concurrent Marking): G1 GC 在整个堆中查找可访问的(存活的)对象。
最终标记(Remark,STW): 该阶段是 STW 回收,帮助完成标记周期。
筛选回收(Cleanup,STW): 筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PAMc0KdR-1592646953867)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646158767-c139dc3f-c978-436f-a9f5-38b0fee25460.png)]
G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字Garbage-First的由来)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了GF收集器在有限时间内可以尽可能高的收集效率。
G1垃圾收集分类
YoungGC
新对象进入Eden区
存活对象拷贝到Survivor区
存活时间达到年龄阈值时,对象晋升到Old区
MixedGC
不是FullGC,回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)
global concurrent marking (全局并发标记)
a) Initial marking phase:标记GC Root,STW
b) Root region scanning phase:标记存活Region
c) Concurrent marking phase:标记存活的对象
d) Remark phase :重新标记,STW
e) Cleanup phase:部分STW
相关参数
a) G1MixedGCLiveThresholdPercent Old区的region被回收的时候的存活对象占比
b) G1MixedGCCountTarget:一次global concurrent marking之后,最多执行Mixed GC的次数
c) G1OldCSetRegionThresholdPercent 一次Mixed GC中能被选入CSet的最多old区的region数量
触发的时机
InitiatingHeapOccupancyPercent:堆占有率达到这个值则触发global concurrent marking,默认45%
G1HeapWastePercent:在global concurrent marking结束之后,可以知道区有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到了此参数,只有达到了,下次才会发生Mixed GC
如何选择垃圾收集器
优先调整堆的大小让服务器自己来选择
如果内存小于100M,使用串行收集器
如果是单核,并且没有停顿时间的要求,串行或JVM自己选择
如果允许停顿时间超过1秒,选择并行或者JVM自己选
如果响应时间最重要,并且不能超过1秒,使用并发收集器
下图有连线的可以搭配使用,官方推荐使用G1,因为性能高
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQt6tTz6-1592646953872)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646186952-0682591d-cc79-47aa-9834-7ebe8323726e.png)]
调优
JVM调优主要就是调整下面两个指标
停顿时间: 垃圾收集器做垃圾回收中断应用执行的时间。-XX:MaxGCPauseMillis
吞吐量: 花在垃圾收集的时间和花在应用时间的占比 -XX:GCTimeRatio=,垃圾收集时间占比:1/(1+n)
GC调优步骤
打印GC日志
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc.log
分析日志得到关键性指标
分析GC原因,调优JVM参数
Parallel Scavenge收集器(默认)
分析parallel-gc.log
第一次调优,设置Metaspace大小:增大元空间大小-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M
第二次调优,添加吞吐量和停顿时间参数:-XX:MaxGCPauseMillis=100 -XX:GCTimeRatio=99
第三次调优,修改动态扩容增量:-XX:YoungGenerationSizeIncrement=30
配置CMS收集器
-XX:+UseConcMarkSweepGC 分析cms-gc.log
配置G1收集器
-XX:+UseG1GC
分析g1-gc.log
查看发生MixedGC的阈值:jinfo -flag InitiatingHeapOccupancyPercent 进程id
分析工具:gceasy,GCViewer
G1调优相关
常用参数
a) -XX:+UseG1GC 开启G1
b) -XX:G1HeapRegionSize=n,region的大小,1-32M,2048个
c) -XX:MaxGCPauseMillis=200 最大停顿时间
d) -XX:G1NewSizePercent -XX:G1MaxNewSizePercent
e) -XX:G1ReservePercent=10 保留防止to space溢出()
f) -XX:ParallelGCThreads=n SWT线程数(停止应用程序)
g) -XX:ConcGCThreads=n 并发线程数=1/4*并行
最佳实践
a) 年轻代大小:避免使用-Xmn、-XX:NewRatio等显示设置Young区大小,会覆盖暂停时间目标(常用参数3)
b) 暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量
是否需要切换到G1
a) 50%以上的堆被存活对象占用
b) 对象分配和晋升的速度变化非常大
c) 垃圾回收时间特别长,超过1秒
G1调优目标
a) 6GB以上内存
b) 停顿时间是500ms以内
c) 吞吐量是90%以上
GC常用参数
堆栈设置
-Xss:每个线程的栈大小
-Xms:初始堆大小,默认物理内存的1/64
-Xmx:最大堆大小,默认物理内存的1/4
-Xmn:新生代大小
-XX:NewSize:设置新生代初始大小
-XX:NewRatio:默认2表示新生代占年老代的1/2,占整个堆内存的1/3。
-XX:SurvivorRatio:默认8表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。
-XX:MetaspaceSize:设置元空间大小
-XX:MaxMetaspaceSize:设置元空间最大允许大小,默认不受限制,JVM Metaspace会进行动态扩展。
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParalledlOldGC:设置并行老年代收集器
-XX:+UseConcMarkSweepGC:设置CMS并发收集器
-XX:+UseG1GC:设置G1收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
并行收集器设置
-XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis:设置并行收集最大暂停时间
-XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
CMS收集器设置
-XX:+UseConcMarkSweepGC:设置CMS并发收集器
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads:设置并发收集器新生代收集方式为并行收集时,使用的CPU数。并行收集线程数。
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理
G1收集器设置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的幂),默认将整堆划分为2048个分区
-XX:GCTimeRatio:吞吐量大小,0-100的整数(默认9),值为n则系统将花费不超过1/(1+n)的时间用于垃圾收集
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor填充容量(默认50%)
-XX:MaxTenuringThreshold:最大任期阈值(默认15)
-XX:InitiatingHeapOccupancyPercen:老年代占用空间超过整堆比IHOP阈值(默认45%),超过则执行混合收集
-XX:G1HeapWastePercent:堆废物百分比(默认5%)
-XX:G1MixedGCCountTarget:参数混合周期的最大总次数(默认8)