Java应用执行非常慢,如何分析

##   机器应用配置

1. 生产环境批量总共4台机器,地址分别为10,11,12,13,
1. 机器配置均为至强96逻辑CPU,252G内存,万兆网卡
1. 每台机器部署6个批量应用服务器,总共25*6*4=600并发
1. 作业配置为4G内存年轻代1536M

##    问题现象:

1. 应用一周左右未重启会出现执行批量特别慢的情况,平时600w数据跑完半小时左右,出问题的应用可能要2个小时甚至更长,重启可以恢复正常,不重启第二天可能还会慢,也有可能会恢复。
1. 出问题的机器,应用,交易不固定,同一台机器某个或某几个应用出问题其他应用正常执行不受任何影响,出问题的应用所有交易执行时间拉长,所有的操作都会变慢,但是能正常跑完。


## 问题分析

1. 之前怀疑是网络,I/O问题,现在基本可以排除,因为观察网络和磁盘io占用很小
1. 怀疑是由于内存占用太高导致的,然后机器内存剩余80G左右,应用内存占用均正常。机器CPU占用70%以下

1. 分析heapdump和gc,jstack日志没有发现明显的异常,应用也没有任何报错,无解。

1. 对出问题的应用使用jmap -histo:live [pid]命令,应用会恢复正常,目前测试了出问题的几个应用,都有效果,但是目前还不清楚原因。

1. 应用FullGc非常少,一天能有一次就不错了。younggc也还好,高频的时候也就30s左右一次,每次29ms这样。

1. 怀疑内存泄露,但是从dump来看并没有什么异常,出问题的应用堆栈基本都在执行数据库操作,就是慢,而且出问题的应用CPU占用比较高达到90%以上。

生产环境,不好测试,问题也不好重现,参数也不能乱改,很迷茫,不知道改怎么解决,有没有什么好的方法或者思路的。。。

gc参数

-server -Xms4096m -Xmx4096m -XX:PermSize=1024M -XX:MaxPermSize=2048m  -Deos.log.home=logs -XX:+UseFastAccessorMethods 
-XX:+UseCompressedOops -XX:+DisableExplicitGC -XX:+ExplicitGCInvokesConcurrent -XX:ParallelGCThreads=10 
-XX:-UseAdaptiveSizePolicy -Xmn1536m -XX:SurvivorRatio=6 -XX:+UseParallelGC -XX:+UseParallelOldGC
 -XX:MaxTenuringThreshold=20 -XX:MaxGCPauseMillis=200 -XX:+PrintAdaptiveSizePolicy -XX:+PrintGCApplicationStoppedTime
 -XX:+PrintGCApplicationConcurrentTime -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:-TraceClassUnloading 
-XX:+PrintGCDetails -Xloggc:gc.log 

分析工具:jmap,jstack,jstat , nmon,nmon分析工具,top,MemoryAnalyzer,具体用法和日志文件分析自行百度

JVM内存结构的知识,堆内存(年轻代,survival区,老年代),非堆内存(方法区,CodeCache,直接内存)

出问题的时候,抓javacore(jstack),查看内存状态(jstat),导出堆内存快照heapdump(jmap),nmon抓日志,top查看状态

主要用于:分析线程死锁,代码问题,内存泄露,资源瓶颈,这些是最常见的问题,也是最先要考虑的。

---------------------------------分割线-------------------------------------------

新发现,控制台有如下警告信息:

Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.
Java HotSpot(TM) 64-Bit Server VM warning: Try increasing the code cache size using -XX:ReservedCodeCacheSize=

查阅资料:

java codeCache

https://blogs.oracle.com/poonam/why-do-i-get-message-codecache-is-full-compiler-has-been-disabled


随着时间推移,会有越来越多的方法被编译,codeCache使用量会逐渐增加,直至耗尽。在codeCache满了之后会发生什么?

在jdk1.7.0_4之前,你会在jvm的日志里看到这样的输出:

Java HotSpot(TM) 64-Bit Server VM warning: CodeCache is full. Compiler has been disabled.

Jit编译器被停止了,并且不会被重新启动。已经被编译过的代码仍然以编译方式执行,但是尚未被编译的代码就只能以解释方式执行了。

针对这种情况,jvm提供了一种比较激进的codeCache回收方式:Speculative flushing。在jdk1.7.0_4之后这种回收方式默认开启,而之前的版本需要通过一个启动参数来开启:-XX:+UseCodeCacheFlushing。在Speculative flushing开启的情况下,当codeCache将要耗尽时,最早被编译的一半方法将会被放到一个old列表中等待回收。在一定时间间隔内,如果方法没有被调用,这个方法就会被从codeCache充清除。

很不幸的是,在jdk1.7中,当codeCache耗尽时,Speculative flushing释放了一部分空间,但是从编译日志来看,jit编译并没有恢复正常,并且系统整体性能下降很多,出现大量超时。在oracle官网上看到这样一个bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8006952 由于codeCache回收算法的问题,当codeCache满了之后会导致编译线程无法继续,并且消耗大量cpu导致系统运行变慢。Bug里影响版本是jdk8,但是从网上其他地方的信息看,jdk7应该也存在相同的问题,并且没有被修复。


做了测试结果,一致,确实会导致性能下降。

查看默认大小:

jinfo -flag XX:ReservedCodeCacheSize 进程号

默认是48M

可以通过-XX:ReservedCodeCacheSize=256M调整大小,可以避开这个问题。

》》》》》》》》》》》》》》》》》》》》》》》》》

那么问题来了,为什么会满呢,到底是什么代码导致的?

增加 -XX:+PrintCompilation  -XX:+CITime 打印JIT编译信息,没什么发现,原始数据太难看了,放弃。


---------------------------------------------必须有更好的工具,最好能直接观察分析---------------------------------------------------------

jconsole可以观察到CodeCache区,但是也只是可以看大小和监控增长情况,不能看内容。

Java应用执行非常慢,如何分析_第1张图片            Java应用执行非常慢,如何分析_第2张图片


JITWatch感觉很牛的样子,github上下载了代码,只需maven编译一下(mvn clean install)然后打开./launchUI.bat就可以用了,要想查看jit编译的代码必须要hsdis才行,查了下oracle并没有提供,没办法下了openjdk的Hotspot代码,编译了一个,一样好使,哈哈哈日志打出来了,里面也有汇编代码。

,这里搬运一下:

编译hsdis:

1.去openjdk官网下载对应的jdk版本的hostspot源码
2.解压源码,找到src/share/tools/hsdis 文件夹
3.查看对应的README,找到对应的binutils版本
4.去官网下载binutils对应版本
5.放到hsdis文件夹下hsdis/build/binutils目录
6.在hsdis文件夹执行make,即可hsdis/build/linux-amd64下会生成hsdis-amd64.so
7.将hsdis-amd64.so放到JVM的libpath里面即可,Linux下为LD_LIBPATH变量指定的位置
8.指定jvm参数-XX:+UnlockDiagnosticVMOptions -XX:+TraceClassLoading -XX:+LogCompilation -XX:+PrintAssembly

9.生成的对应hotspot_pid.log文件里就会打印汇编代码和编译信息!

这里有个坑,我下的版本源码的makefile是有错误的,不过我在GitHub上找了个大神弄的,同样可以编译出来,然后把makefile替换掉也可以了。

附录:
openjdk hotspot源代码下载(我这里下的是jdk7的,根据需要)

http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/tags

jitwatch地址:

https://github.com/AdoptOpenJDK/jitwatch

----------------------------------------------------------------2018年5月22日23:49:55---------------------------------------------------------------------

接下来如何分析呢?

//TO BE CONTINUED


哈哈哈,最后啥也没分析出来,可能还是本身平台的问题,用的cglib代理比较多,动态生成类占用jit太多?

总之把默认CodeCache改成256m了,目前没有再出现问题。



你可能感兴趣的:(Java应用执行非常慢,如何分析)