以前原本打算结合本身写的小程序来介绍JConsole和VisualVM的使用的,可是发现很难经过一个程序把全部的场景都体现出来,因此仍是决定用书中的典型小例子来说更加清晰。 java
JConsole是一个机遇JMX(Java Management Extensions,即Java管理扩展)的JVM监控与管理工具,监控主要体如今:堆栈内存、线程、CPU、类、VM信息这几个方面,而管理主要是对JMX MBean(managed beans,被管理的beans,是一系列资源,包含对象、接口、设备等)的管理,不只能查看bean的属性和方法信息,还可以在运行时修改属性或调用方法。
首先咱们看下JConsole的启动,JConsole在jdk/bin/下,其启动须要图形界面的支持(废话,都说了图形界面),可能很多人一听到这个就以为有点low:平时服务器跑的linux都没图形界面,那岂不是用不了。其实不用担忧,JConsole支持远程进程监测。下边是链接界面,其实至关于jps命令:
linux
再来看下链接后的界面,咱们打开DeadLock(一个测试死锁的示例): 算法
能够看到上边的选项卡正好对应各个功能。
小程序
下边来看两个小示例,分别分析内存和死锁的。 缓存
package com.gj.jconsole; import java.util.ArrayList; import java.util.List; public class DataInsert { //一个OOMObject实例大概64k+ static class OOMObject{ public byte[] placeholder= new byte[64*1024]; } public static void fillHeap() throws InterruptedException { List<OOMObject> list =new ArrayList<OOMObject>(); for(int i=0;i<1000;i++){ Thread.sleep(100); list.add(new OOMObject()); } System.gc(); } public static void main(String[] args) throws Exception{ fillHeap(); } }能够看到程序向list中插入了1000个OOMObject对象,每一个OOMObject大概64k,那么堆内存的峰值应该在64k*1000=64m左右,咱们运行程序,并使用JConsole打开DataInsert进程,当程序结束时堆内存以下:
能够看到对内存峰值在60-70m之间(下方已用内存为63631kb,大约63m),与咱们预计的相符。下边咱们来看下下边这段代码: 服务器
package com.gj.jconsole; import java.util.ArrayList; import java.util.List; import com.gj.jconsole.DataInsert.OOMObject; public class GCTest { // 一个OOMObject实例大概640k+ static class OOMObject { public byte[] placeholder = new byte[64 * 1024*10]; } public static void fillHeap() throws InterruptedException { for(int i=0;i<100;i++){ OOMObject oOmObject =new OOMObject(); Thread.sleep(1000); oOmObject=null; } } public static void main(String[] args) throws InterruptedException { fillHeap(); } }这段代码每次新建一个OOMObject对象,在暂停1s后将其置null,咱们来看下运行时内存图:
会发现堆内存呈规律的折线,咱们来分析下:当每一个对象实例化后,而后置null,这时候对象并不会被回收(由于没有gc),所以内存会一直上升,可是当堆内存不够用时,会触发gc,所以内存会下降。查看VM概况可知,一共进行了18次gc,回收算法为“复制”。 多线程
package com.gj.jconsole; public class DeadLock { static class SynAddRunable implements Runnable{ int a,b; public SynAddRunable(int a,int b){ this.a= a; this.b= b; } @Override public void run() { synchronized (Integer.valueOf(a)) { synchronized (Integer.valueOf(b)){ System.out.println(a+b); } } } } /** * @param args */ public static void main(String[] args) { for(int i=0;i<100;i++){ new Thread(new SynAddRunable(1, 2)).start(); new Thread(new SynAddRunable(2, 1)).start(); } } }能够看到代码中启动200个子线程,进行1+2或2+1的计算,可是这种状况为何会出现死锁呢?咱们看到在run中出现双重sychronized,这是典型的死锁特征, 可是这种状况要出现死锁前提是多线程中 sychronized同步的两个对象分别都是同一个,才会形成互锁,可是Integer.valueOf(a)和Integer.valueOf(b) 每次返回的不都是一个新对象吗?这里须要注意一个问题,为了节省内存,对于[-128,127]之内的转换, Integer.valueOf会将这些值从缓存直接返回,因此相同的值返回的都是同一个对象(记得看java源码的时候见过不少这种处理方法)。 好了,来看下如何检查死锁。
以上就是JConsole的基本用法,仍是比较简单的。但这些只是小道,工具毕竟只是辅助,关键的仍是要懂得原理,学会分析,真正的能在实践中活用才好。下次将介绍更为强大的VisualVM,敬请期待喽^_^。 ide