原博客地址:http://zorufa876.iteye.com/blog/625649
最近在学TDA(Thread Dump Analyzer)的时候,发现一款很好用的查看JVM的工具–VisualVM,这个工具是Sun在JDK1.6 Update7之后的版本中推出的,就放在bin目录下面,惭愧的是我竟然一直都没发现。
简单说来,VisualVM是jConsole的升级版,但它可比jConsole好用多了。它能为您提供强大的thread 和heap分析能力。它囊括的命令行工具包括 JConsole, jstack,jstat, jmap ,jps。具体怎么操作我就不说了,很简单,大家可以参考这几个网址:
VisualVM入门指南:https://visualvm.dev.java.net/zh_CN/gettingstarted.html
VIsualVM介绍: http://www.iteye.com/topic/516447
jstatd介绍: http://java.sun.com/javase/6/docs/technotes/tools/share/jstatd.html
下面我就提一下我在学习操作这个工具的过程中遇到的几个问题,不过在看问题一和问题二之前最好先阅读一下jstatd介绍会比较好。
问题一 :在服务端启动jstatd的时候,我执行的命令如下:jstatd -J-Djava.security.policy=jstatd.all.policy
我发现它会报
java.rmi.ConnectIOException: non-JRMP server at remote endpoint
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:230)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:184)
at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:322)
at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
at java.rmi.Naming.rebind(Naming.java:160)
at sun.tools.jstatd.Jstatd.bind(Jstatd.java:40)
at sun.tools.jstatd.Jstatd.main(Jstatd.java:126)
这个错误,而报这个错误的
原因是因为我在上面的命令中没有指定端口号,所以jstatd就采用默认的端口号1099,可是由于这个端口号已经被别的程序给占用了,所以会报上面这个 错误。因此,我就用指定的端口号来执行jstatd
比如:jstatd -J-Djava.security.policy=jstatd.all.policy -p 2222,问题就解决了。
问题二 :在执行了jstatd -J-Djava.security.policy=jstatd.all.policy -p 2222这个命令以后,后面又用ctrl+C把它给stop了,那么你再重新启动2222这一端口的jstatd的话,你会发现VisualVM调用不到 这个jstatd了,并且服务器过一会儿就会报:
java.rmi.ConnectIOException: error during JRMP connection establishment; nested exception is:
java.net.SocketTimeoutException: Read timed out
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:286)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:184)
at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:322)
at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
at java.rmi.Naming.rebind(Naming.java:160)
at sun.tools.jstatd.Jstatd.bind(Jstatd.java:40)
at sun.tools.jstatd.Jstatd.main(Jstatd.java:126)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
at java.io.BufferedInputStream.read(BufferedInputStream.java:237)
at java.io.DataInputStream.readByte(DataInputStream.java:248)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:228)
… 6 more
这样的错误。这是因为虽然jstatd被stop了,但是这个端口的进程还是存在的,所以要么你再换一个没用过的端口号,要么你把原来的还在被占用的端口 号给kill掉,然后重新启动jstack,再重新启动VisualVM,就可以了。
参考资料:http://java.sun.com/j2se/1.4.2/docs/api/java/lang/reflect/InvocationTargetException.html
http://blog.csdn.net/dingyuan963/archive/2009/07/09/4333071.aspx
会操作工具了,就来实际模拟一下怎么找bug吧,
一:查找线程死锁:
先写一段死锁的代码
直接eclipse里面运行,启动VisualVM的远程监控。
你会很容易的发现在VisualVM的“线程”tab中有两条线程的颜色特别鲜艳,如下图:
很明显,Thread-1和Thread-0是有问题的两条线程:
再按一下上图中的“线程 dump”按钮,找到这两个线程的详细信息:
可以看出互相锁住了资源
二:查找内存异常:
同样的,从激烟那里搞来他上次分享时写的能占用很多内存的代码
直接debug运行,在String s="test"这里打个断点,然后查看VisualVM中“监视”这个tab
要想确定到底是调用哪个方法导致堆内存占用太多,需要点击“堆 dump”按钮(注1:),这一步需要一些时间,得到如下页面:
通过“大小”的排序,发现java.util.Long所占用的内存是最大的。好,就查这个类的实例,双击它(注2:),得到如下页面:
然后由于这些实例是从小到大排序的,所以找到最后一个实例,如图:
可以发现testMemory就是那个占用内存最大的实例了。
最后还想再提醒一句 ,VisualVM的功能是不止这些的,点击菜单栏上的工具->插件选项,你会发现 VisualVM还有很多好用的插件没安装,比如VisualVM-TDA-Module这个插件,它就是整合了TDA的功能的,至于其他的各位好好去发 掘吧。
注1: 对于“堆 dump”来说,在远程监控jvm的时候,VisualVM是没有这个功能的,只有本地监控的时候才有。另外,就算是本地监控,它在dump和得到实例的 速度那是相当的慢的。所以鉴于这几个原因,不建议用VisualVM,而是用jmap加上Mat来分析内存情况。