什么时候需要调优
- 非计算密集型任务cpu占用过高
- 老年代已使用空间大于70%
- Full GC频繁
- 单次GC时间大于1秒
- 出现OOM
- 程序的响应速度明显变慢
情况
非计算密集型任务cpu占用过高
:有用户线程cpu过高、gc线程cpu过高。用户线程cpu过高一般是出现了死循环,需要查看线程堆栈、结合arthas的watch命令等找出问题根源。gc线程过高一般是Full GC频繁,Full GC频繁一般是因为老年代或方法区(元空间)内存不足。需要对gc日志分析、对堆转储文件分析,确定是哪些对象实例占用了过多内存、反射的类是否过多、被多个类加载器加载的类是否过多等,决定是增加内存大小还是对源码进行处理等。老年代已使用空间大于70%
:有可能发生了内存泄漏、有可能是设置的堆大小无法满足正常业务的需求。需要对堆转储文件分析,确定是哪些对象实例占用了过多内存,并检查是否发生了内存泄漏。对堆大小无法满足正常业务的需求,应调大堆大小,通过-Xms和-Xmx来设置。Full GC频繁
:Full GC频繁一般是因为老年代或方法区(元空间)内存不足。需要对gc日志分析、对堆转储文件分析,确定是哪些对象实例占用了过多内存、反射的类是否过多、被多个类加载器加载的类是否过多等,决定是增加内存大小还是对源码进行处理等。单次GC时间大于1秒
:
- 老年代内存过大。老年代中累积了大量对象时才触发full gc,这次gc的任务很重,耗费的时间很长。通过测试,选择一个合适的老年代大小。
- 年轻代过小。年轻代过小,当年轻代空间不足时对象就会分配到老年代,而这些对象可能是“短命”的,但会停留在老年代很久。当full gc时,gc的任务会很重。因此,可以增大年轻代的大小。通过增大-XX:MaxTenuringThreshold=n来设置对象的晋升到老年代的年龄门槛,减缓对象进入老年代。
- 创建对象的速度过快。对创建对象过快的代码进行优化,防止短时间内大量创建对象,以致年轻代空间不足而提前进入老年代。
- gc线程数过少。gc线程数过少,无法充分利用多核cpu的并行处理。gc日志中 user、sys、real时间需要关注。若real时间 与 (user时间/gc线程数+sys)相差不远,则gc线程数合适。
- 进程被swap出内存。当物理内存不足时,系统会将一些内存存于磁盘的swap区,而swap区的访问速度慢很多。检查进程是否被放到swap区了,若是,应该增大物理内存,减少其他进程对内存的占用。
- 合适的垃圾收集器。若非jvm专家,建议使用G1。通过-XX:MaxGCPauseMillis来设置最大停顿时间。
出现OOM
:老年代或方法区(元空间)内存不足。开启-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path>。利用mat(memory analizer tool)对堆转储文件进行分析,确定是哪些对象实例占用了过多内存、反射的类是否过多、被多个类加载器加载的类是否过多等,决定是增加内存大小还是对源码进行处理等。程序的响应速度明显变慢
:检查是否存在上面的四种情况或可能出现了死锁。
调优工具
jps
:可查看java进程的id、主类名、启动参数等。jstat
:可查看堆的情况、gc的统计数据等。
例如
jstat -gc 1000 10 # 每隔1000ms输出一次gc和堆情况,共输出10次
jstat -gcutil 1000 10 # 每隔1000ms输出一次gc和堆的已使用百分比,共输出10次。utilization 利用率
jstat -gcnewcapacity 1000 10 # 输出gc和年轻代的情况
其他用法请自行百度搜索jstat详细用法
jmap
:可用于查看堆整体情况、对象的histogram图、类加载器统计信息、finalizer的信息、dump出堆转储文件。
jmap -dump:live,format=b,file=heap.hprof
jhat
:对堆转储文件进行分析,可以查看所有类、所有类的实例数量、GC Roots能到达的对象、堆实例histogram图、支持OQL(Object Query Language)。jstack
:查看某个java进程的所有的线程的堆栈信息、每个线程的锁信息。能发现死锁。mat(memory analizer tool)
:对堆转储文件进行分析,可以查看堆实例histogram图、每个对象到GC Roots的链、内存泄漏分析等。内存泄露分析很强大。推荐使用。arthas
:阿里开源的java诊断程序,功能丰富强大,能方便的监控某个方法、发现死锁、热更新代码等。推荐使用。jvisualvm
:可查看启动参数、系统属性、cpu的情况、加载、卸载的类的情况、线程数量、堆空间情况、元空间的情况、动态采用堆对象的histogram图。可以下载其他插件来丰富功能,如visualgc插件运行动态可视化观察堆的各个区域的变化。jprofiler
:收费软件。优秀的java性能分析器。能查看cpu、堆、线程、类等的整体情况。能筛选出增量的堆对象,并对增量的对象进行分析,是jprofiler的一大特色。能动态观察monitor和锁的情况。能观察实时堆对象、能创建堆快照并进行分析。还有很多功能,十分强大。
参数
-XX被称为不稳定参数,对该类型参数的设置对jvm的性能有较大影响。
-XX语法规则
布尔值
-XX:+ 设置该参数值为true
-XX:- 设置该参数值为false
数字、字符串值
-XX:<param-name>=<value>
参数 | 作用 | 示例 |
---|---|---|
-Xms | 指定初始堆大小 | -Xms100m、-Xms4g |
-Xmx | 指定堆的上限大小,推荐设置为和-Xmn一样,若大于-Xmn,则jvm会根据需要动态调整堆大小,而这需要额外的性能开销 | -Xmx100m、-Xmx4g |
-Xmn | 指定年轻代的大小 | -Xmn100m |
-Xss | 指定虚拟机栈的大小 | -Xss100m |
-XX:NewRatio=n | 指定年轻代和老年代的比例,年轻代:老年代=1:n | -XX:NewRatio=6 |
-XX:SurvivorRatio=n | 指定Eden区和Survivor区的比例,Eden区:Survivor 1: Survivor 2=n:1:1 | -XX:SurvivorRatio=5 |
-XX:+HeapDumpOnOutOfMemory | 开启发生OOM时dump出堆转储文件 | -XX:+HeapDumpOnOutOfMemory |
-XX:HeapDumpPath | 指定堆转储文件的位置 | -XX:HeapDumpPath=<path> |
-Xloggc | 适用于jdk8及之前,指定gc日志保存位置。适用于jdk8及之前开启gc日志,需要-Xloggc和-XX:+PrintGC | -Xloggc:<path> |
-Xlog:gc*:file=<path> | jdk9及以后开启gc日志 | |
-XX:+PrintGC | 开启打印GC简单日志 | |
-XX:+PrintGCDetails | 开启打印gc详细日志 | |
-XX:MaxMetaspaceSize | 设置元空间的上限大小 | -XX:MaxMetaspaceSize=100m |
-XX:MaxTenuringThreshold | 年轻代中的对象晋升到老年代的年龄门槛 | -XX:MaxTenuringThreshold=20 |
-XX:+UseParallel | 年轻代使用PS收集器 | |
-XX:+UseParallelOldGC | 老年代使用PS收集器 | |
-XX:ParallelGCThreads | PS收集器的gc线程数 | -XX:ParallelGCThreads=5 |
-XX:MaxGCPauseMillis | gc的最大停顿时间 | |
-XX:MaxDirectMemorySize | 设置直接内存的大小。当报OutOfMemoryError: direct buffer memory时调大这个值 | -XX:MaxDirectMemorySize4g |
-XX:+DisableExplicitGC | 禁止运行期显示调用System.gc() |
案例
用户线程CPU占用过高
一般是代码问题,出现了死循环。
示例代码
public void