问题 & 目标
JVM问题:CPU load过高、请求延迟、TPS降低、内存泄漏
调优目标:使用较小的内存占用获得较高的吞吐量 或者 较低的延迟
调优指标:
- 内存占用:程序正常运行需要的内存大小
- 延迟:由于垃圾收集而引起的程序停顿时间
- 吞吐量:用户程序运行时间占用用户程序和垃圾收集占用总时间比值
参考日志
- 系统运行日志:程序代码打印的日志,描述了代码级别的系统运行轨迹
- 堆栈错误信息:根据堆栈信息可以初步定位问题所在
- GC日志:程序启动时用
-XX:+PrintGCDetails
和-xloggc:/data/jvm/gc.log
把程序运行时GC过程记录下来,通过GC日志分析每块内存区域的GC的频率、时间,从而发现问题所在 - 线程快照:通过
jstack pid
,可以dump出当前进程中线程的快照信息,通过观察线程在某一时刻的状态,如果系统中存在请求超时、死循环、死锁情况时,可以根据快照进一步确定问题。 - 堆转储快照:程序启动时使用
-XX:+HeapDumpOnOutOfMemory
和-XX:HeapDumpPath=/data/jvm/dumpfile.hprof
命令,当程序发生内存溢出时,把当时的内存快照以文件形式转储,事后对当时的内存使用情况分析
JVM调优工具
jps
命令:查看JVM启动的所有进程、执行主类的全名、JVM启动参数jstat
命令:监视虚拟机信息,例jstat -gc pid 500 10
(每500ms打印各个区容量、使用容量、gc时间,打印10次)jmap
命令:查看堆内存信息,例jmap -histo pid
(打印当前堆中每个类的实例数量和内存占用)jhat
命令:通过浏览器分析对应的内存快照,例jhat -port 9810 -J-Xmx4G /data/jvm/dumpfile_jmap.hprof
(以9810端口启动jhat内嵌的服务器)jconsole / jvisualvm:分析内存信息(各个区内存变化情况)
JVM调优经验
说明:
- 一般情况采用默认配置,在测试中根据系统运行状况和GC日志、内存监控进行合理调整
- 新生代越大,老年代越小,Full GC 频率越高,但 Full GC 时间短
- 新生代越小,老年代越大,Full GC 频率越低,但 Full GC 时间长
参数设置:
-Xms
和-Xmx
的值设成相等,Heap不够用时,会发生内存抖动,影响程序运行稳定性
堆大小默认为-Xms
指定大小;
默认空闲堆内存小于40%时,JVM会扩大堆到-Xmx
指定的大小;
空闲堆内存大于70%时,JVM会减小堆到Xms
指定大小;
如果在 Full GC 后满足不了内存需求会动态调整,这个阶段比较耗费资源新生代尽量设置大一些,让对象在新生代多存活一段时间,每次 Minor GC 都要尽可能多的收集垃圾对象,防止或延迟对象进入老年代的机会,从而减少 Full GC 的频率
老年代如果使用CMS收集器,新生代可以不用太大(CMS并行收集速度很快,可以和用户线程并发执行)
方法区大小设置,1.6之前需要考虑系统运行时动态增加的常量、静态变量,1.7只要能装下类信息就可以
代码实现:
- 避免创建过大的对象及数组:过大对象在新生代没有足够空间会直接进入老年代,如果是短命大对象,会提前触发 Full GC
- 避免同时加载大量数据:可以分批读取,用完后尽快清空引用
- 用完尽快清空集合中对象的引用: 尽快回收,避免进入老年代
- 在合适场景采用软引用、弱引用:内存溢出前会把它们列入回收范围进行二次回收
- 避免产生死循环:死循环产生后,可能产生大量实例,导致空间占满
JVM参数
参数 | 说明 | 实例 |
---|---|---|
-Xms |
初始堆大小,默认物理内存的 1/64 | -Xms512M |
-Xmx |
最大堆大小,默认物理内存的 1/4 | -Xmx2G |
-Xmn |
新生代内存大小,官方推荐为整个堆的 3/8 | -Xmn512M |
-Xss |
线程堆栈大小,jdk1.5及之后默认1M,之前默认 256k | -Xss512k |
-XX:NewRatio=n |
设置新生代和年老代的比值。例:3,表示年轻代与年老代比值为1:3 | -XX:NewRatio=3 |
-XX:SurvivorRatio=n |
年轻代中Eden区与两个Survivor区的比值。例:8,表示Eden:Survivor=8:1:1 | -XX:SurvivorRatio=8 |
-XX:PermSize=n |
永久代初始值,默认为物理内存的 1/64 | -XX:PermSize=128M |
-XX:MaxPermSize=n |
永久代最大值,默认为物理内存的 1/4 | -XX:MaxPermSize=256M |
-verbose:class |
在控制台打印类加载信息 |
|
-verbose:gc |
在控制台打印垃圾回收日志 |
|
-XX:+PrintGC |
打印GC日志,内容简单 |
|
-XX:+PrintGCDetails |
打印GC日志,内容详细 |
|
-XX:+PrintGCDateStamps |
在GC日志中添加时间戳 |
|
-Xloggc:filename |
指定gc日志路径 -Xloggc:/data/jvm/gc.log | “ |
-XX:+UseSerialGC |
年轻代设置串行收集器Serial |
|
-XX:+UseParallelGC |
年轻代设置并行收集器Parallel Scavenge |
|
-XX:ParallelGCThreads=n |
设置Parallel Scavenge收集时使用的CPU数。并行收集线程数 | -XX:ParallelGCThreads=4 |
-XX:MaxGCPauseMillis=n |
设置Parallel Scavenge回收的最大时间(毫秒) | -XX:MaxGCPauseMillis=100 |
-XX:GCTimeRatio=n |
设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) | -XX:GCTimeRatio=19 |
-XX:+UseParallelOldGC |
设置老年代为并行收集器ParallelOld收集器 |
|
-XX:+UseConcMarkSweepGC |
设置老年代并发收集器CMS |
|
-XX:+CMSIncrementalMode |
设置CMS收集器为增量模式,适用于单CPU情况 |
|
参考:
https://blog.csdn.net/huyuyang6688/article/details/81490570