生产环境,不好测试,问题也不好重现,参数也不能乱改,很迷茫,不知道改怎么解决,有没有什么好的方法或者思路的。。。
-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区,但是也只是可以看大小和监控增长情况,不能看内容。
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---------------------------------------------------------------------
接下来如何分析呢?
哈哈哈,最后啥也没分析出来,可能还是本身平台的问题,用的cglib代理比较多,动态生成类占用jit太多?
总之把默认CodeCache改成256m了,目前没有再出现问题。