神刀安全网

另一份Java应用调优指南之-工具篇

Java应用的调优,再不写都要忘光了,先对付着写完,以后再出更新版。

前一篇是三个月前的 另一份Java应用调优指南 - 前菜

1. 土法调优两大件

一般人在没有Profile工具的时候,调优的两大件,无非Heap Dump 与 Thread Dump。

1.1 Heap 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插件打开,对象的大小有两种统计方式:

  • 本身大小(Shallow Size):对象本来的大小。
  • 保留大小(Retained Size): 当前对象大小 + 当前对象直接或间接引用到的对象的大小总和。

看本身大小时,占大头的都是char[] ,byte[]之类的,没什么意思(用jmap -histo:live pid 看的也是本身大小)。所以需要关心的是保留大小比较大的对象,看谁在引用这些char[], byte[]。

(VisualVM用法:命令行输入jvisualvm,文件->装入->堆Dump->检查 -> 查找20保留大小最大的对象,就会触发保留大小的计算,然后就可以类视图里浏览,按保留大小排序了)

1.2 Thread Dump

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停顿久很多。

线程状态里有几点注意的地方:

状态:

  • RUNNABLE: 运行中状态,可能里面还能看到locked字样,表明它获得了某把锁。
  • BLOCKED:被某个锁(synchronizers)給block住了。
  • WAITING:等待某个condition或monitor发生,一般停留在park(), wait(), sleep(),join() 等语句里。
  • TIME_WAITING:和WAITING的区别是wait() 等语句加上了时间限制 wait(timeout)。

分析工具:

2. 你真正要的Java Mission Control

2.1 优点

代替收费的JProfiler的好东西,以前Bea JRockit的宝贝,现在随着JDK7 up40以后的版本免费自带了。

另一个让人开心的事情就是JMC采用采样,而不是传统的代码植入的技术,对应用性能的影响非常小,完全可以开着JMC来做压测,不会像以前,开了植入型的Profiler,出来的结果差了一个数量级不说,热点完全可能是错误的,这是一个真实的故事,具体细节就不说了。

2.2 功能

JMC里可以看的东西太多了,我自己认为最有用的包括:

  • 内存:分配里的按类、按线程来归类的对象分配情况,对象的分配调用栈,让对象分配无处躲藏。
  • 内存:GC情况
  • 代码:热点方法及它的调用栈,超有用的功能。调用树是从线程类的角度看的方法调用分配
  • 线程:热点线程,再换个姿势来看热点方法和调用树。
  • 线程: 争用,等待时间,锁定实例等。

2.3 使用方法简述

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中展示。

这是一篇严肃的文章,就不配图了。

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 另一份Java应用调优指南之-工具篇

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址