概要说明
目前我们生产环境中经常会发现CPU以及内存占用很高,甚至出现java.lang.OutOfMemoryError:PermGenspace java.lang.OutOfMemoryError: Java heap space等异常,以及因为cpu过高导致的响应缓慢及假死等问题,经过这段时间的跟进发现当前生产环境上未对JVM的参数进行优化,相应的监控及异常机制也做的很少,出现问题时我们无法快速定位问题的原因。为了后续方便定位JVM的相关问题,保证线上的稳定运行,特整理此文档。
问题分析
我这里梳理出一些目前常见的问题以及出现相关问题时的分析思路和方法:
- java.lang.OutOfMemoryError:PermGenspace
说明是Java虚拟机对永久代Perm内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方jar包,调整持久代的内存空间大小即可
- java.lang.OutOfMemoryError: Java heap space
这个问题是我们需要非常关注的问题,出现这种问题时就需要我们根据导出dump文件以及对应gc日志进行分析,后面会重点介绍。
- CPU占用过高的常见原因
- 繁忙的IO操作(网络IO或磁盘IO,常见的文件的频繁读写如SFTP,文件的上传下载,文件的频繁读取)。
- 高密度的计算 (针对计算密集型程序,会出现算力不足,任务越多效率越低,例如长时间的循环计算、视频的解码,死循环等)。
- 不合理线程的配置(线程的配置和CPU和内核数及程序运行时间存在关系,并不是越大越好,不合理的配置会导致内存和cpu的高负载运行)
- 缺乏CPU资源。
- 内存占用过高的原因
- 内存泄露,对象已经死了,无法通过垃圾收集器进行自动回收,久而久之,内存泄漏的对象堆积起来,会导致物理机的内存被耗尽,出现OOM报错。(找出泄露的代码位置和原因,才好确定解决方案,常见的原因有未关闭的流操作)
- 内存溢出,内存中的对象都还必须存活着,这说明Java堆分配空间不足,但是不能一味的增加堆分配的空间解决问题,需要结合代码查看是否存在问题:
- 检查代码是否存在对象生命周期太长、持有状态时间过长的情况
- 线程死锁、锁争用、大量的线程等待
- 线程运行时间过长
以上描述的问题经常都是CPU的突然提升,往往也伴随着内存的占用升高,所以我们在分析问题时,通常都是一起去分析。
问题解决方法论
优化JVM启动的参数配置
首先需要优化JVM启动参数的配置,指定对应堆内存大小(值得大小可根据实际服务器及应用情况进行调整),GS的收集日志,以及异常时dump文件的生成,方便后续问题的排查处理,如下为我整理的常用配置,对于特定需求和应用需要进行调整优化,另外针对JDK的版本不同设置也会不一样,不能一概而论。例如JDK1.8版本默认的垃圾回收机制为G1,更早的版本不是,还有Xss1m等,都需要更具自己的版本进行查看。
参数的优化是一个持续的过程,要定时监控服务的线上运行情况,根据实际情况调整如下配置的参数以达到最优化的处理。
推荐的优化配置:
JAVA_OPTS="-Xms2g -Xmx2g -Xmn512m -Xss256k -XX:PermSize=128M -XX:MaxPermSize=258m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:/data/log/gclog/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/log/jvmdump/jvm.hprof "
参数解析说明:
-Xms、-Xmx :其中–Xms为JVM初始的内存大小,-Xmx为JVM最大可用内存。
这两个参数一定要设置,而且建议设置成相等,不能使用默认值,以避免每次垃圾回收完成后JVM重新分配内存,如果不设置,可能会出现频繁Full GC。说明:如果需要使用G1的一些自动设置参数,则该项不能如此进行设置。
-Xmn:是年轻代堆内存大小。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。这里可根据程序的特点进行适量增加,但尽量不要超过整个堆得2/3. 针对高并发和吞吐量的系统建议此值可以设置大些。
-Xss: 线程栈内存大小,如果减少线程栈的大小,可以使剩余的系统内存支持更多的线程;
-XX:PermSize:是持久代初始的分配大小。慎用最小限制选项PermSize以节约系统资源
-XX:MaxPermSize:是持久代的最大允许分配尺寸,按需分配。过小会导致:java.lang.OutOfMemoryError: PermGen space。
以上参数为启动JVM虚拟机最常用的参数,以下参数是针对特定的场景和需求添加的
GC收集主要的相关配置如下,其他配置参数可根据实际需要额外添加:
-XX:+PrintGCDetails:打印gc详细信息,XX:+PrintGC输出GC日志但不够详细。
-XX:+PrintGCDateStamps:打印gc时间
-XX:+PrintHeapAtGC:打印GC前后的详细堆栈信息,可根据需要选用
-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析,注意此写入是否覆盖写入的。需提前创建好对应的目录