Java性能优化常用命令及工具简介
简述
开发中可能都会碰到程序运行越来越慢,占用内存和cpu越来越高,那如何定位问题呢?
我们将问题简单分类为三个常见的场景
1,多线程或边界情况等引起的异常
2,内存异常
3,CPU异常
首先这三个问题肯定是会相互影响的,我们只是为了方便表述,将问题进行了非常简单的分类而已。
(文中出现的pid都是需要替换为具体进程号)
1.多线程或边界情况等引起的异常
很多时候后面两种问题的原因都是第一种情况影响的,尤其是没有做个详尽的压力测试的情况。所以第一种情况我们最好防患于未然,白盒测试,压力测试搞起来。
可以用Jmeter来进行压力测试,在项目中第一个基础模块出来以后就将JMeter工程准备好。每过一段时间或每次重大版本更新都将Jmeter工程维护起来,然后进行压力测试。绝对能够发现非常多的问题,也会避免非常多的上线以后的雷。
2.内存异常
内存异常主要表现为内存持续升高或者反映在CPU上的GC过于频繁或持stop the world time时间太长。这时候我们需要查看进程中的对象个数和GC频率与耗时。
jmap命令可以方便的查看内存中对象的个数和大小。
jmap -histo pid | more
生成内容如下图所示,可见String对象现在有812003个。
但是这些对象不见得是所有存活的对象,有些生命周期已经结束,但是还未到GC的时候,所以并未被回收。可以通过
jmap -histo:live pid | more
来强制执行一次GC再统计内存中的对象数。结果如下图所示,
如果GC完以后还有明显的超标对象,那基本就可以确定是哪个对象的生命周期没有控制好了。
或者也可能是另外一种情况,就是查看内存中的对象的时候有些对象的数量变化特别剧烈,可能仅仅间隔10秒的两次jmap -histo pid命令就可以看到以百千万记的对象数量变动,这种变化也会反映在GC频率上。
jstat -gc pid 5000
如下图,从倒数第五列YGC可知,约每15秒执行了一次young gc
综上,不被回收的和剧烈变化的对象都是我们要重点追查的对象。那问题来了,代码中可能有几十处在用这个对象,究竟是那儿没有控制好呢?
这时候我们可以用Jprofiler来分析堆内存文件或attach到该进程来查看。例如我们可以先用
jmap -dump:format=b,file=20200124.jmap.hprof pid
下载堆内存文件然后进行分析。Jprofiler的具体用法就不赘述了。可以用incoming references来找到具体类对该对象的引用所占百分比。非常贴心了~
3. CPU问题
CPU问题常见有死锁、某方法耗时过长
首先我们可以用
top -Hp pid
来找到占用cpu高的线程。
如我们要查看13089的线程为什么占用了90%(原谅我假装将2.3%看成了90%)。首先我们需要将线程ID转为16进制,因为用jstack命令查看的线程ID都是16进制的。可以用直接在shell中输入printf “%x\n” 13089进行转化如图
然后用
jstack -l 13055 | grep -A10 -B4 3321
来查看这个线程在干嘛,通常一次看到的内容可能并不太准确,需要多执行几次。如果多次观察发现该状态经常为Blocked或者虽然是Runnable但是进程停留在一个方法。说有锁设计不合理,或者Runnable停留的那个方法耗时很长。
总结
jmeter
jmap
jmap -histo pid |more
jmap -histo:live pid | more
jmap -dump:format=b,file=2020.jmap.hprof pid
Jprofiler
jstat
jstat -gc pid 5000
top -Hp pid
printf "%x\n" threadId
jstack -l pid | grep -A12 ’tId\|running’
以上就是常用的性能优化或者问题排查时候需要用的基本命令或工具。希望对您也有所帮助。