Java应用的调优,再不写都要忘光了,先对付着写完,以后再出更新版。
前一篇是三个月前的 另一份Java应用调优指南 - 前菜
一般人在没有Profile工具的时候,调优的两大件,无非Heap Dump 与 Thread Dump。
jmap -dump:live,format=b,file=heap.hprof pid
从安全点的日志看,从Heap Dump开始,整个JVM都是停顿的,考虑到IO(写到Page Cache,或许触发background flush),几G的Heap可能产生几秒的停顿,在生产环境上执行时谨慎再谨慎。
live的选项,实际上是产生一次Full GC来保证只看还存活的对象。有时候也会故意不加live选项,看历史对象。
Dump出来的文件建议用JDK自带的VisualVM或Eclipse的MAT插件打开,对象的大小有两种统计方式:
看本身大小时,占大头的都是char[] ,byte[]之类的,没什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要关心的是保留大小比较大的对象,看谁在引用这些char[], byte[]。
(VisualVM用法:命令行输入jvisualvm,文件->装入->堆Dump->检查 -> 查找20保留大小最大的对象,就会触发保留大小的计算,然后就可以类视图里浏览,按保留大小排序了)
ThreadDump 同样会造成JVM停顿,在生产系统上执行谨慎又谨慎。
有两种方式执行thread dump,一种是大家都知道的命令行方式,"jstack pid” 或"jstack -l pid" ,-l 会同时打印各种lock,但会使得JVM停顿得更久,慎用。
另一种是直接用代码来打印,比如如果代码里线程池满了无法添加新任务,可以在代码里直接把当前线程情况打印出来
ThreadMXBean threadMBean = ManagementFactory.getThreadMXBean();ThreadInfo[] threadInfos = threadMBean.dumpAllThreads(false, false);
可以直接调用ThreadInfo的toString()把它打印出来,但里面设置了stack层数最大只有8,如果想看更源头的调用栈,需要自己把它的toString 函数复制出来做一个工具方法,取消掉层数限制。
同样注意,这里threadMBean.dumpAllThreads(false,false)的参数为false,把参数改为true,则打印synchronizers与monitor,同样使得JVM停顿久很多。
线程状态里有几点注意的地方:
状态:
分析工具:
代替收费的JProfiler的好东西,以前Bea JRockit的宝贝,现在随着JDK7 up40以后的版本免费自带了。
另一个让人开心的事情就是JMC采用采样,而不是传统的代码植入的技术,对应用性能的影响非常小,完全可以开着JMC来做压测,不会像以前,开了植入型的Profiler,出来的结果差了一个数量级不说,热点完全可能是错误的,这是一个真实的故事,具体细节就不说了。
JMC里可以看的东西太多了,我自己认为最有用的包括:
JDK7在启动服务时加上-XX:+UnlockCommercialFeatures -XX:+FlightRecorder ,JDK8则不需要。如果是远程服务器,要开JMX
“-Dcom.sun.management.jmxremote.port=7001 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=127.0.0.1”
JDK自带的jmc命令,文件->连接->设定JMX连接,启动飞行纪录,固定时间选1分钟或更多,事件设置选为profiling,然后进一步修改,自己查看下都Profile了哪些信息,觉得不够的再添加些(下次就直接用上次设定就好了),然后就开始Profile,1分钟后Profile结束,会自动把记录下载回来,在JMC中展示。
这是一篇严肃的文章,就不配图了。