初识JVM

1.调优调的什么

调的大部分都是堆,虽然偶尔也会因为虚拟机栈或元空间导致的OOM异常,但这是极少数故不做讨论

目的:多做YGC,少做Full GC。尽可能的将短命对象在年轻代时就被回收

2.个人常用调优参数

标准: - 开头,所有的hotspot 支持
非标准:-X 开头,特定hotspot支持
不稳定:-XX开头,下个版本可能取消

参数含义
-Xmx50m最大堆小设置为50m (默认系统的1/4)
-Xms20m最小堆小设置为20m (默认操作系统的1/64)
-Xmn20m新生代大小设置为20m
-Xss1m栈内存设置为1m
-XX:MetaspaceSize=128m初始化元空间大小为128m
-XX:MaxMetaspaceSize=128m最大元空间大小
-XX:MaxTenuringThreshold=15新生代设置年龄为15。CMS默认为6,其他默认为15
-XX:SurvivorRatio=8新生代设置为8:1:1
-XX:-UseAdaptiveSizePolicy禁用Survivor区自适应策略
-XX:TargetSurvivorRatio=80Survivor区对象使用率80%,默认是50%,如果超过则将超过某个年龄的对象一起放入老年代
-XX:+DisableExplicitGC禁止用System.gc(),防止频繁Full GC
-XX:PretenureSizeThreshold=5242880指定对象超过5M后直接进入老年代。前提是:ParNew + CMS组合
–XX:+PrintGC打印GC日志
-XX:+PrintGCDetails打印详细GC日志
-XX:+PrintGCDateStamps打印发生的GC时间 34.629
-XX:+PrintGCDateStamps打印发生的GC时间戳 2021-05-14T17:23:06.324+0800 。注如果2个时间一起使用,以当前这个为准
-XX:+PrintHeapAtGC打印GC前后的详细堆栈信息
-XX:PrintFLSStatistics=1打印每次GC前后内存碎片的统计信息
-XX:+HeapDumpOnOutOfMemoryError当发生OOM时,dump内存文件
-XX:HeapDumpPath=D:\workspace\gc-dump.hprof dump将dump文件输出到某目录
-Xloggc:D:\workspace\fsk-workorder\gc-%t.log将GC日志输出到文件中
-XX:+UseGCLogFileRotation开启GC日志分割
-XX:NumberOfGCLogFiles=14最多分割几个文件,超过之后从头文件开始写
-XX:GCLogFileSize=100M每个文件上限大小,超过就触发分割
-XX:+PrintTenuringDistribution打印对象分布
-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p发生OOM时触发脚本,%p是线程ID

2.1 针对CMS参数配置

以下内容摘抄《深入理解Java虚拟机》p98.
由于CMS是一款基于 “标记-清除”算法实现的收集器,就意味着有大量内存碎片产生,空间碎片过多时,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很多的剩余空间,但就是无法找到足够大的连续空间来分配对象,而不得不提前触发一次Full GC的情况。为了这决这个问题,CMS收集器提供了一个 -XX:+UseCMSCompactAtFullCollection 开关参数(默认开启,此参数从JDK1.9开始废弃),用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程,由于这个内存整理需要移动对象,在(Shenandoah 和 ZGC出现前)是无法并发的,这样空间碎片文件是解决了,但停顿时间又会变长,因此虚拟机又设置了另外一个参数:-XX:CMSFullGCsBeforeCompaction(JDK1.9后废弃),这个参数作用是要求CMS收集器在执行过若干次(数量由数值绝对)不整理空间的Full GC之后,下一次进行Full GC前会先进行碎片整理(默认值为0,表示每次进入Full GC时都会进行碎片整理)

参数含义
-XX:+UseConcMarkSweepGC新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction设置CMS收集器在老年代空间被使用多少后触发,默认92%。要是CMS运行期间预留的内存无法满足程序分配新对象的需要,就会出现一次“并发失败 (Concurrent Mode Failure”),这时候虚拟机将不得不启动后备预案:冻结用户线程的执行,临时启用 Serial Old收集器来重新进行老年代的垃圾收集,但这样停顿时间就很长了。所以该参数设置的过高将会容易导致大量的并发失败产生,性能反而降低,用户应在生产环境中根据实际应用情况还权衡配置
-XX:+UseCMSCompactAtFullCollection设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理,默认开启,JDK1.9后废弃。由于这个内存整理需要移动对象,在(Shenandoah 和 ZGC出现前)是无法并发的,这样空间碎片文件是解决了,但停顿时间又会变长,因此虚拟机又设置了另外一个参数:-XX:CMSFullGCsBeforeCompaction
-XX:CMSFullGCsBeforeCompactionJDK1.9后废弃。设定进行多少次CMS垃圾回收后,进行一次内存压缩,默认为0,表示每次进行FULL GC时都会进行碎片处理
-XX:+CMSClassUnloadingEnabled允许对类元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction当永久区占用率达到这一百分比时,启动CMS回收
-XX:UseCMSInitiatingOccupancyOnly表示只在到达阀值的时候,才进行CMS回收

个人服务器JVM配置:

-Xmx2048M -Xms2048m -Xmn600m -XX:MetaspaceSize=128m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC  -XX:PretenureSizeThreshold=5242880 
-XX:CMSInitiatingOccupancyFraction=80 -XX:CMSFullGCsBeforeCompaction=3 -XX:+UseCMSInitiatingOccupancyOnly 
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\workspace\fsk-workorder\gc-dump.hprof -XX:+PrintGCDetails -XX:+PrintGCTimeStamps 
-Xloggc:D:\workspace\fsk-workorder\gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=2 -XX:GCLogFileSize=50M 

垃圾收集器组合

参数含义优劣
-XX:+UseSerialGCSerial + Serial Old: DefNew:新生代,老年代都使用串行回收收集器单核服务器推荐使用
-XX:+UseParallelGC -XX:+UseParallelOldGCPS +PO:新生代,老年代都使用并行回收收集器注重吞吐量
-XX:+UseParNewGC -XX:+UseConcMarkSweepGCParNew + CMS:新生代使用并行收集器,老年代使用串行回收收集器注重响应时间
-XX:+UseG1GCG1垃圾收集器

常用命令

参数含义
java -XX:+PrintCommandLineFlags查看GC信息及采用的垃圾收集器
java -XX:+PrintFlagsInitial查看ava 环境初始默认值

2.模拟OOM

这里结合 JPofiler 分析工具进行JVM分析。JProfiler下载地址

下面代码模拟OOM异常,并设置jvm运行参数

-Xmx10m -Xms10m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails

//-Xmx10m -Xms10m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
public class HellocGC {

    public static void main(String[] args) {
        List cList = new LinkedList();
        for (;;) {
            byte[] b = new byte[1024 * 1024];
            cList.add(b);
        }
    }
}

程序运行之后:

[GC (Allocation Failure) [PSYoungGen: 2048K->504K(2560K)] 2048K->1013K(9728K), 0.0011957 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 1765K->504K(2560K)] 8419K->7381K(9728K), 0.0014081 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 504K->502K(2560K)] [ParOldGen: 6877K->6813K(7168K)] 7381K->7315K(9728K), [Metaspace: 3240K->3240K(1056768K)], 0.0083864 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 1526K->1526K(2560K)] [ParOldGen: 6813K->6813K(7168K)] 8339K->8339K(9728K), [Metaspace: 3240K->3240K(1056768K)], 0.0070806 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 1526K->1514K(2560K)] [ParOldGen: 6813K->6804K(7168K)] 8339K->8319K(9728K), [Metaspace: 3240K->3240K(1056768K)], 0.0095279 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid1972.hprof ...
Heap dump file created [9480656 bytes in 0.015 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at cn.sys.Test.test.HellocGC.main(HellocGC.java:17)
Heap
 PSYoungGen      total 2560K, used 1661K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 81% used [0x00000000ffd00000,0x00000000ffe9f488,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 6804K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 94% used [0x00000000ff600000,0x00000000ffca5240,0x00000000ffd00000)
 Metaspace       used 3323K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 358K, capacity 388K, committed 512K, reserved 1048576K

2.1分析GC日志

Minor GC:

[GC (Allocation Failure) [PSYoungGen: 2048K->504K(2560K)] 2048K->1013K(9728K), 0.0011957 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

参数含义
GC年轻代中的minor GC
Allocation Failure年轻代中没有足够的区域用于存放需要分配的数据而导致的异常
PSYoungGen收集器方式:采用PS+PO方式
2048K->504K(2560K)2048k:GC前新生代占用空间大小; 504K:GC后新生代占用空间大小,这里指Survivor幸存者区,因为Ygc会将Eden区存活的对象复制到Survivir幸存者区,此时Eden区空间大小为0k。2560K:年轻代总容量
2048K->1013K(9728K)2048k:GC前堆占用空间大小(新生代 + 老年代); 1013K:GC后堆占用空间大小(新生代 + 老年代) 9728K:堆总空间大小(新生代 + 老年代)
[Times: user=0.00 sys=0.00, real=0.01 secs]用户态耗时、内核态耗时、总耗时

生产环境GC信息:

[GC [PSYoungGen: 300865K->6577K(310720K)] 392829K->108873K(417472K), 0.0176464 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]

 PSYoungGen: 新生代使用多线程垃圾收集器
 300865K:GC前整个新生代占用空间大小。当GC后,这里为0kb
 6577K:  GC后新生代占用空间大小,这里指幸存者区。
 310720K:新生代总大小
 392829K:GC前堆占用空间大小。新生代 + 老年代
 108873K: GC后堆占用空间大小。新生代 + 老年代
 417472K:堆空间大小。新生代 + 老年代
 GC前后对比:
GC前老年代占用空间大小:392829-300865=91964  约等于老年代占用了堆大小为89M
 GC前新生代占用空间大小:300865  新生代大小为:383M
 GC后老年代占用空间大小:108873-6577=102296   99M
 垃圾:392829 - 108873 = 381946  垃圾为:372M 

Full GC

[Full GC (Ergonomics) [PSYoungGen: 1526K->1526K(2560K)] [ParOldGen: 6813K->6813K(7168K)] 8339K->8339K(9728K), [Metaspace: 3240K->3240K(1056768K)], 0.0070806 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

参数含义
Full GC年轻代 + 老年代的中的Major GC
Ergonomics晋升到老生代的平均数据大小大于老生代的剩余内存大小
其它其它参数同上

汇总GC原因

参数含义
GC年轻代中的minor GC
Full GC年轻代 + 老年代的饿中的major GC 。 指STW
Allocation Failure年轻代中没有足够的区域用于存放需要分配的数据而导致的异常。加大年轻代大小:-xmn400m
Ergonomics晋升到老生代的平均数据大小大于老生代的剩余内存大小。加大年轻代大小:-xmn400m
promotion failedDefNew:晋升到老生代的平均数据大小大于老生代的剩余内存大小。加大年轻代大小:-xmn400m
Metadata GC Threshold元空间引发的GC。元空间指向的是本地内存,默认大小为21M 加大初始化元空间大小: -XX:MetaspaceSize=128m

堆详细日志

Heap
 PSYoungGen      total 2560K, used 1661K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 81% used [0x00000000ffd00000,0x00000000ffe9f488,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 6804K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 94% used [0x00000000ff600000,0x00000000ffca5240,0x00000000ffd00000)
 Metaspace       used 3323K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 358K, capacity 388K, committed 512K, reserved 1048576K
参数含义
PSYoungGen total 2560K, used 1661K年轻代中的总大小为2560k、已使用1661k
eden space 2048K, 81% usededen区 总大小 2048k、已使用81%
from(to) space 512K, 0%幸存者区 总大小 512k、已使用 0%
ParOldGen total 7168K, used 6804K老轻代中的总大小为7168k、已使用6804k
object space 7168K, 94% used
Metaspace used 3323K, capacity 4496K committed 4864K, reserved 1056768K元空间总大小为3323k、已使用4496k
class space used 358K, capacity 388K, capacity 388K, committed 512K, reserved 1048576K

3.dump分析

我们已经有了dump文件,那我们应该如何分析它呢,这里采用JProfiler工具打开,可以快速定位问题
在这里插入图片描述

在这里插入图片描述
由以上信息得出具体出错的位置。

JProfiler + Java VisualVM 工具分析dump

1.场景及jvm配置:

启动一个spring boot项目,并设置 -Xmx100m -Xms100m -Xmn50m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:HeapDumpPath=D:\workspace\fsk-workorder\gc-dump.hprof

2.代码示例

现在模拟一个OOM,代码如下所示:

在这里插入图片描述

3.Java VisualVM监控图:

在这里插入图片描述

4.dump文件分析

在这里插入图片描述
文章内容皆是自己理解所写,可能会不严谨,如有错误,欢迎指出,谢谢

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值