1、垃圾收集器
1.1、serial收集器
单线程
作用域: 新生代
算法: 标记-复制算法
1 | -XX:+UseSerialGC |
1.2、ParNew收集器
多线程
作用域: 新生代
算法: 标记-复制算法
1 |
|
1.3、Parallel Scavenge收集器
多线程
作用域: 新生代
算法: 标记-复制算法
高吞吐量: Parallel Scavenge 重点关注的是程序达到一个可控制的吞吐量(Thoughput,吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)),高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务;
特点:
如何达到控制吞吐量?
1.4、Serial Old收集器
单线程
作用域: 老年代
算法: 标记-整理算法
1.5、Parallel Old收集器
多线程
作用域: 老年代
算法: 标记-整理算法
1.6、CMS收集器
多线程
作用域: 老年代
算法: 标记-清除算法
CMS 运行过程分为以下 4 个阶段:
初始标记:标记 GC Roots 能直接关联的对象(速度很快,需要暂停所有的工作线程);
并发标记:进行 GC Roots 跟踪(和用户线程一起工作,不需要暂停工作线程);
重新标记:修正因用户程序继续运行而导致标记产生变动的那一部分对象的标记(需要暂停所有的工作线程);
并发清除:清除 GC Roots 不可达对象(和用户线程一起工作,不需要暂停工作线程)。
1 | -XX:+UseConcMarkSweepGC |
CMS核心参数
1.-XX:+UseConcMarkSweepGC:启用cms;
2.-XX:ConcGCThreads:并发的GC线程数;
3.-XX:+UseCMSCompactAtFullCollection:FullGC之后做压缩整理(整理碎片,也就是浮动垃圾);
4.-XX:CMSFullGCsBeforeCompaction:多少次FullGC之后压缩一次,(默认是0,代表每次FullGC后都会压缩一次/错误知识点);
5.-XX:CMSInitiatingOccupancyFraction: 当老年代内存使用达到该比例时会触发FullGC(默认是92,这是百分比)==> 解决concurrent mode failure问题, 让其不会等到内存快用完了,才去做full gc
6.-XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阈值(-XX:CMSInitiatingOccupancyFraction设
定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整百分比;
7.-XX:+CMSScavengeBeforeRemark:在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段;
8.-XX:+CMSParallellnitialMarkEnabled:表示在初始标记的时候多线程执行,缩短STW;
9.-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行,缩短STW;
1.7、G1收集器
无分代: G1 将新生代,老年代的物理空间划分取消了。这样我们再也不用单独的空间对每个代进行设置了,不用担心每个代内存是否足够;
标记-整理算法: G1 收集器采用标记-整理算法,无内存碎片产生;
分区回收: G1 虽然没有了新生代与老年代的物理限制,但是 G1 采取内存分区策略,将堆内存划分为大小固定的几个独立区域。在分区中,同时存在新生代与老年代;
停顿时间(可以用JVM参数 -XX:MaxGCPauseMillis指定)来制定回收计划
可以用参数”-XX:G1HeapRegionSize”手动指定Region大小
G1 垃圾收集分类
1、YoungGC
YoungGC并不是说现有的Eden区放满了就会马上触发,G1会计算下现在Eden区回收大概要多久时间,如果回收时间远远小于参数 -XX:MaxGCPauseMills 设定的值,那么增加年轻代的region,继续给新对象存放,不会马上做Young GC,直到下一次Eden区放满,G1计算回收时间接近参数 -XX:MaxGCPauseMills 设定的值,那么就会触发Young GC;
2、MixedGC
不是FullGC,老年代的堆占有率达到参数(-XX:InitiatingHeapOccupancyPercent)设定的值则触发,回收所有的Young和部分Old(根据期望的GC停顿时间确定old区垃圾收集的优先顺序)以及大对象区,正常情况G1的垃圾收集是先做MixedGC,主要使用复制算法,需要把各个region中存活的对象拷贝到别的region里去,拷贝过程中如果发现没有足够的空region能够承载拷贝对象就会触发一次Full GC;
3、Full GC
停止系统程序,然后采用单线程进行标记、清理和压缩整理,好空闲出来一批Region来供下一次MixedGC使用,这个过程是非常耗时的。(Shenandoah优化成多线程收集了) ;
G1收集器参数设置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的线程数量
-XX:G1HeapRegionSize:指定分区大小(1MB~32MB,且必须是2的N次幂),默认将整堆划分为2048个分区
-XX:MaxGCPauseMillis:目标暂停时间(默认200ms)
-XX:G1NewSizePercent:新生代内存初始空间(默认整堆5%)
-XX:G1MaxNewSizePercent:新生代内存最大空间
-XX:TargetSurvivorRatio:Survivor区的填充容量(默认50%),Survivor区域里的一批对象(年龄1+年龄2+年龄n的多个年龄对象)总和超过了Survivor区域的50%,此时就会把年龄n(含)以上的对象都放入老年代
-XX:MaxTenuringThreshold:最大年龄阈值(默认15)
-XX:InitiatingHeapOccupancyPercent:老年代占用空间达到整堆内存阈值(默认45%),则执行新生代和老年代的混合收集(MixedGC),比如我们之前说的堆默认有2048个region,如果有接近1000个region都是老年代的region,则可能 就要触发MixedGC了
-XX:G1MixedGCLiveThresholdPercent(默认85%) region中的存活对象低于这个值时才会回收该region,如果超过这 个值,存活对象过多,回收的的意义不大。
-XX:G1MixedGCCountTarget:在一次回收过程中指定做几次筛选回收(默认8次),在最后一个筛选回收阶段可以回收一会,然后暂停回收,恢复系统运行,一会再开始回收,这样可以让系统不至于单次停顿时间过长。
-XX:G1HeapWastePercent(默认5%): gc过程中空出来的region是否充足阈值,在混合回收的时候,对Region回收都 是基于复制算法进行的,都是把要回收的Region里的存活对象放入其他Region,然后这个Region中的垃圾对象全部清理掉,这样的话在回收过程就会不断空出来新的Region,一旦空闲出来的Region数量达到了堆内存的5%,此时就会立即停止混合回收,意味着本次混合回收就结束了。
垃圾收集器 | 分类 | 作用位置 | 使用算法 | 特点 | 适用场景 | 别名 |
---|---|---|---|---|---|---|
Serial | 串行 | 新生代 | 复制算法 | 响应速度优先 | 适用于单CPU环境下的client模式 | Copy |
ParNew | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境Server模式下与CMS配合使用 | ParNew |
Parallel | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 | PS Scavenge |
Serial Old | 串行 | 老年代 | 标记-整理(压缩)算法 | 响应速度优先 | 适用于单CPU环境下的Client模式 | MarkSweepCompact |
Paraller Old | 并行 | 老年代 | 标记-整理(压缩)算法 | 吞吐量优先 | 适用于后台运算而不需要太多交互的场景 | PS MarkSweep |
CMS | 并发 | 老年代 | 标记-清除算法 | 响应速度优先 | 适用于互联网或B/S业务 | ConcurrentMarkSweep |
G1 | 并发、并行 | 新生代、老年代 | 标记-整理(压缩)算法 | 响应速度优先 | 响应速度优先 | G1 Mixed Generation |
补充学习
https://blog.csdn.net/weixin_46209120/article/details/132812808
1.8 收集器组合
Young | Tenured | JVM options | Description |
---|---|---|---|
Serial | Serial Old | -XX:+UseSerialGC | 单线程进行GC,适合单CPU或小内存,单机程序 |
Serial– | CMS+SerialOld | -XX:-UseParNewGC -XX:+UseConcMarkSweepGC | CMS进行GC失败时,会自动使用Serial Old 策略进行GC;JDK8中声明为废弃 |
Parallel Scavenge | Serial Old | -XX:+UseParallelGC | Jdk1.5及之前版本的搭配使用 |
Parallel Scavenge | Parallel Old | -XX:+UseParallelGC -XX:+UseParallelOldGC | 适合多CPU,需要最大吞吐量,如后台计算型应用;是JDK8默认收集器策略 |
Parallel New– | Serial Old | -XX:+UseParNewGC | JDK8中声明为废弃 |
Parallel New | CMS+SerialOld | -XX:+UseParNewGC -XX:+UseConcMarkSweepGC | 适合多CPU,追求低停顿时间,需快速响应如互联网应用 |
G1 | -XX:+UseG1GC | JDK9默认收集器 | |
ZGC | -XX:+UseZGC |
2、JVM jdk自带工具
2.1 jmap
jmap是java内存映像工具,主要用于查询当前堆和方法区的详细信息,生成堆的快照文件等。一般都是使用-XX:+HeapDumpOnOutOfMemoryError
参数指定JVM在内存溢出异常时自动生成堆的快照文件。之后在服务器发生内存溢出异常时,将对应的快照文件拉取到本地使用工具分析。
常用命令
1 | 查看java堆使用情况 |
2.2 jstack
jstack用于生成java虚拟机当前时刻线程快照,可定位线程死锁,死循环
常用命令
1 | jstack pid |
3、其他工具
3.1 Arthas
在命令行下面执行(使用和目标进程一致的用户启动,否则可能 attach 失败):
1 | curl -O https://arthas.aliyun.com/arthas-boot.jar |
3.1.1 dashboard
1 | dashboard |
3.1.2 jad
反编译文件
1 | jad <全路径名> |
3.1.3 trace
方法耗时跟踪
1 | trace <全路径名> <方法名> |
4、问题
如何调优?
1、调整堆内存的大小
2、调整垃圾回收器
3、设置新生代和老年代比例
4、设置GC线程数
6、使用合适数据结构,减少对象创建
1、设置基础JVM参数
设置内存大小参数
设置元空间大小参数 -XX:MaxMetaspaceSize 最大值:防止向操作系统内存中占用大小 一般256m -XX:MetaspaceSize 这个值代表进行fullgc清理预值 两者一样不会发生fullgc
设置虚拟机栈大小 -Xss 根据操作系统值不一样 x86_64 1M
不建议手动调节
- -Xmn 设置年轻代大小,默认为整个堆大小的1/3(G1不建议)
- -XX:MaxTenuringThreshold 最大晋升值
- -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=
- -XX:+PrintGCDetails -XX:PrintGCDateStamps -Xloggc:文件路径
2、利用良好数据结构,减少对象创建
3、更换匹配的垃圾回收器
4、优化垃圾回收器参数
垃圾回收器如何选择?
1、优先调整堆的大小让JVM自己选择
2、内存很小或者单核机器直接串行 或 JVM自己选择
3、如果允许停顿时间超过1秒,选择并行或者JVM自己选
4、如果响应时间最重要,并且不能超过1秒,使用并发收集器
5、4G以下可以用parallel,4-8G可以用ParNew+CMS,8G以上可以用G1,几百G以上用ZGC