Java性能测试相关小结

        近期公司进行了相关项目的性能测试,经过大半个月的折腾,总算学习了不少相关经验,在此记录分享一番


1.  主要工具

1.1 jvisualvm

       JDK自带的图形化工具,总体来说在性能监控方面要比JConsole好一些,原理都是类似,需要通过JMX等进行JVM分析,  为此如需远端访问,则需要在目标JAVA程序启动时添加如下参数:

-Djava.rmi.server.hostname=[本机IP]
-Dcom.sun.management.jmxremote.port=[监控端口]
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

        其它的配置就不在此赘述,相信大家都很容易连上待监测的目标程序,具体界面如下所示:

1.2 jstack

   JDK自带工具,用于导出线程信息(也可通过jvisualvm图形化方式导出),具体命令如下:

jstack -l [进程ID]

1.3 jmap

   JDK自带工具,用于导出JVM堆内存信息(也可通过jvisualvm图形化方式导出),具体命令如下:   

jmap -dump:format=b,file=[文件路径] [进程ID]

1.4 jinfo

    JDK自带工具,可动态修改JVM参数,在生产环境下监控问题有时需要,具体命令如下:

jinfo -flag [+|-][name] [进程ID]
jinfo -flat [name]=[value] [进程ID]

1.5 JProfile

     大家都说好,单具体还没仔细研究,留待后续补充。。。


2.  场景与策略

2.1 CPU冲高、响应缓慢

       这种情况大部分是由于Full GC导致的,具体判断可以通过JVisualVM工具观察内存占用情况,如占用过高,且呈锯齿状,则很可能就是频繁Full GC了。如果JAVA程序启动时加上了如下参数,则可通过观察日志确认是否有Full GC发生(日志会打出[Full GC xxxxx]):

-XX:+PrintGCTimeStamps -XX:+PrintGCDetails

   如果启动参数中未加上GC日志,则可通过jinfo动态修改,如下所示:

jinfo -flag +PrintGCDetails [进程ID]

2.2 JAVA堆错误

   在生产环境中,很可能由于内存泄漏或高并发导致内存回收后,剩余仍不足2%的情况,此时就会产生如下错误:

java.lang.OutOfMemoryError: Java heap space

   所以,为了能够及时分析堆内存,可在Java程序启动参数中添加堆Dump,如下所示:

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=%CATALINA_BASE%/logs/dumpfile

2.3 线程分析

      在确认Full GC问题之后,先撇开内存泄漏问题,很可能是并发过高导致线程过多,而各线程中的变量占用空间较大从而触发了Full GC。具体确认方法可以通过jstack或者jvisualvm导出线程信息(最好通过top -Hp [进程ID] 打印出所有线程的CPU占用情况),具体要关注两种线程:

  1. 占用CPU过高的线程,通过top -Hp找出占用CPU较多的线程ID,并转换为16进制数,然后在线程信息中找到对应线程,确认该线程具体问题点(大部分占CPU较多的,此时可能都是GC线程);
  2. 状态为BLOCKED的线程,一半BLOCKED都是由于锁竞争导致的,此时线程过多必然导致处理时间变长,从而大量变量得不到回收;为此,加快线程处理速度是很好的解决方案,具体可参考java.util.concurrent包下的各解决方案,总的策略是如必须使用锁,则该锁的粒度应尽量小。

       如果看上去各个线程均正常,而且线程池未耗尽,然而却触发了Full GC,则可能是由于业务线程中的临时对象过多过大导致的,确认方法也比较简单,使用jmap或者jvisualvm导出业务执行前后的堆Dump信息,比较内存与对象增长情况。此时,具体的优化策略有如下几种:

  1. 从业务上减少大量数据的情景,如利用分页分段等方式获取数据;
  2. 对于变化不频繁的数据,可使用全局缓存,从而尽量减少查询以及局部变量;
  3. 对于大的临时对象,尽早的将其引用置为NULL可加快内存回收(此处在《深入理解Java虚拟机》中有说明)。

     除了上述状况外,如果仍然业务响应较慢,则需要从单业务流程的角度进行优化,例如使用缓存尽量减少数据库交互等方式。

2.4 内存泄漏分析

     如长时间运行出现Java堆溢出、内存随业务增长且回收不掉等情况,很可能是内存泄漏导致,主要策略如下:

  1. 运行待测试业务一段时间(保证字符串常量池等稳定便于后续分析),通过jvisiualvm执行垃圾回收后dump出堆文件;
  2. 在此运行测试业务,并同样执行垃圾回收并dump出堆文件;
  3. 利用jvisualvm中查看堆文件的类,并将两个堆文件进行比较从而确认对象的主要增长点和疑似泄漏点,从而进行进一步确认与分析,如下图所示


      上述只是简单介绍,其实定位问题过程还是挺麻烦的,后续要研究JProfile等工具,看是否能高效定位问题。

发布了42 篇原创文章 · 获赞 9 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/jjxojm/article/details/89647107