如果大家有遇到过Java内存洩露问题而且亲自动手去定位和分析经历的同学来讲获取Java的堆内信息对了内存使用情况的问题分析和定位是非常有帮助了例如我们常用的MAT工具可以较方便的让我们定位程序中内存的使用情况是哪块导致了内存的洩露等
但由于传统的分析过程比较麻烦需要使用Jdk的jmap(Java Memory Map)命令把heap内存dump到一个文件然后用MAT进行分析所以本文介绍一种方法可以实现在线查看heap内存的使用情况并附上源码实现希望对大家有帮助由于目前调研中只找到了Sun JDK以及以上版本的实现所以目前该方案只支持Sun JDK或以上如果其他同学有其它版本的JDK实现分享欢迎一起交流
整体实现思路如下
JDK中在toolsjar类库里有一个comsuntoolsattachVirtualMachine类该类可以获得JVM虚拟机的相关控制权限
利用getPidsexe或其它工具获取需要监控的JVM 的pid进程号信息
利用反射调用VirtualMachine的attach方法获取VirtualMachine的实例对象
复用反射调用VirtualMachine实例的heapHisto方法参数为 –all 可获到JVM的堆内存信息
最后解析heapHisto方法返回的输入流读取内存数据即可获得当前JVM的堆内存数据
下面帖出的主要代码来说明各步骤具体实现方法
l JDK中在toolsjar类库里有一个comsuntoolsattachVirtualMachine类该类可以获得JVM虚拟机的相关控制权限
private static Class<?> findVirtualMachineClass() throws ClassNotFoundException
MalformedURLException {
// JVM 虚拟机操作类
final String virtualMachineClassName = comsuntoolsattachVirtualMachine;
try {
return ClassforName(virtualMachineClassName)
} catch (final ClassNotFoundException e) {
// exception ignored try looking else where
File file = new File(SystemgetProperty(javahome))
if (jreequalsIgnoreCase(filegetName())) {
file = filegetParentFile()
}
//直接从JDK的 lib目录下加载 toolsjar类库
final String[] defaultToolsLocation = { lib toolsjar };
for (final String name : defaultToolsLocation) {
file = new File(file name)
}
final URL[] urls = { filetoURI()toURL() };
final ClassLoader cl = URLClassLoadernewInstance(urls)
//再次尝试反射查询 JVM虚拟机操作类
return ClassforName(virtualMachineClassName true cl)
}
}
l 利用反射调用VirtualMachine的attach方法获取VirtualMachine的实例对象
本过程相对比较简单获取VirtualMachine的类后根据反射类查询attach方法
//获取JVM 虚拟机操作类后
final Class<?> virtualMachineClass = findVirtualMachineClass()
//根据反射查询 attach方法参数为String类型
final Method attachMethod = virtualMachineClassgetMethod(attach Stringclass)
//通过 getpidsexe工具获取当前JVM进程号
final String pid = PIDgetPID()
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 = virtualMachineClassgetMethod(heapHisto
Object[]class)
//该方面返回值为InputStream
return (InputStream) invoke(heapHistoMethod getJvmVirtualMachine()
new Object[] { new Object[] { all } })
l 最后解析heapHisto方法返回的输入流读取内存数据即可获得当前JVM的堆内存数据 通过该方法返回的一个文本内容
Input Stream取出的结果(文本内容)示例如下
num #instances #bytes class name
: [C
: [B
: <symbolKlass>
: javalangString
: [I
: <constMethodKlass>
: <methodKlass>
: [LjavalangObject;
: <constantPoolKlass>
: javalangStringBuilder
: <instanceKlassKlass>
: javautilTreeMap$Entry
: <constantPoolCacheKlass>
: [S
: sunjvmstatperfdatamonitorAliasFileParser$Token
: javanioHeapCharBuffer
Total
null
因为内容太长只截取了部分
因为读取的是文本内容而且格式是完全固定的所以大家可以直接解析里面的内容主要是后面三例一个是实现的个数一个是实际的数据内容最后一个是实例关联的类名称