如果大家有遇到过Java内存泄露问题,而且亲自动手去定位和分析经历的同学来讲,获取Java的堆内信息对了内存使用情况的问题分析和定位是非常有帮助了。例如我们常用的MAT工具,可以较方便的让我们定位程序中内存的使用情况,是哪块导致了内存的泄露等。
但由于传统的分析过程比较麻烦,需要使用Jdk的jmap(Java Memory Map)命令把heap内存dump到一个文件,然后用MAT进行分析。所以本文介绍一种方法可以实现在线查看heap内存的使用情况,并附上源码实现,希望对大家有帮助。由于目前调研中只找到了Sun JDK6以及以上版本的实现,所以目前该方案只支持Sun JDK6或以上。如果其他同学有其它版本的JDK实现分享,欢迎一起交流。
整体实现思路如下:
1. JDK6中在tools.jar类库里有一个com.sun.tools.attach.VirtualMachine类,该类可以获得JVM虚拟机的相关控制权限。
2. 利用getPids.exe或其它工具获取需要监控的JVM 的pid进程号信息
3. 利用反射调用VirtualMachine的attach方法,获取VirtualMachine的实例对象
4. 复用反射调用VirtualMachine实例的heapHisto方法,参数为 –all, 可获到JVM的堆内存信息
5. 最后解析heapHisto方法返回的输入流,读取内存数据即可获得当前JVM的堆内存数据
下面帖出的主要代码来说明各步骤具体实现方法:
l JDK6中在tools.jar类库里有一个com.sun.tools.attach.VirtualMachine类,该类可以获得JVM虚拟机的相关控制权限。
private static Class<?> findVirtualMachineClass() throws ClassNotFoundException, MalformedURLException { // JVM 虚拟机操作类 final String virtualMachineClassName = "com.sun.tools.attach.VirtualMachine"; try { return Class.forName(virtualMachineClassName); } catch (final ClassNotFoundException e) { // exception ignored, try looking else where File file = new File(System.getProperty("java.home")); if ("jre".equalsIgnoreCase(file.getName())) { file = file.getParentFile(); } //直接从JDK的 lib目录下加载 tools.jar类库 final String[] defaultToolsLocation = { "lib", "tools.jar" }; for (final String name : defaultToolsLocation) { file = new File(file, name); } final URL[] urls = { file.toURI().toURL() }; final ClassLoader cl = URLClassLoader.newInstance(urls); //再次尝试反射查询 JVM虚拟机操作类 return Class.forName(virtualMachineClassName, true, cl); } } |
l 利用反射调用VirtualMachine的attach方法,获取VirtualMachine的实例对象
本过程相对比较简单,获取VirtualMachine的类后,根据反射类,查询attach方法
//获取JVM 虚拟机操作类后 final Class<?> virtualMachineClass = findVirtualMachineClass(); //根据反射查询 attach方法,参数为String类型 final Method attachMethod = virtualMachineClass.getMethod("attach", String.class); //通过 getpids.exe工具获取当前JVM进程号 final String pid = PID.getPID(); try { //通过反射调用attache方法 jvmVirtualMachine = invoke(attachMethod, null, pid); } finally { enabled = jvmVirtualMachine != null; } |
l 复用反射调用VirtualMachine实例的heapHisto方法,参数为 –all, 可获到JVM的堆内存信息
本过程也是利用反射调用heapHisto方法,实现的代码如下:
final Class<?> virtualMachineClass = getJvmVirtualMachine().getClass(); //反射调用 heapHisto方法,参数为 -all final Method heapHistoMethod = virtualMachineClass.getMethod("heapHisto", Object[].class); //该方面返回值为InputStream return (InputStream) invoke(heapHistoMethod, getJvmVirtualMachine(), new Object[] { new Object[] { "-all" } }); |
l 最后解析heapHisto方法返回的输入流,读取内存数据即可获得当前JVM的堆内存数据, 通过该方法返回的一个文本内容。
Input Stream取出的结果(文本内容)示例如下:
num #instances #bytes class name ---------------------------------------------- 1: 14948 1892768 [C 2: 958 567568 [B 3: 1870 215584 <symbolKlass> 4: 7366 176784 java.lang.String 5: 132 88104 [I 6: 841 86360 <constMethodKlass> 7: 841 67672 <methodKlass> 8: 968 61152 [Ljava.lang.Object; 9: 101 48152 <constantPoolKlass> 10: 2593 41488 java.lang.StringBuilder 11: 101 40600 <instanceKlassKlass> 12: 952 30464 java.util.TreeMap$Entry 13: 79 25112 <constantPoolCacheKlass> 14: 310 22280 [S 15: 746 17904 sun.jvmstat.perfdata.monitor.AliasFileParser$Token 16: 367 17616 java.nio.HeapCharBuffer Total 39840 3645336 null |
因为内容太长,只截取了部分
因为读取的是文本内容,而且格式是完全固定的,所以大家可以直接解析里面的内容。主要是后面三例,一个是实现的个数,一个是实际的数据内容,最后一个是实例关联的类名称.
Good Luck!
Yours Matthew!
posted on 2013-02-20 16:30
x.matthew 阅读(6280)
评论(9) 编辑 收藏 所属分类:
Best Practise(JDK API)