`

转:实现实时获取Java堆内存信息

阅读更多

 

   如果大家有遇到过Java内存泄露问题,而且亲自动手去定位和分析经历的同学来讲,获取Java的堆内信息对了内存使用情况的问题分析和定位是非常有帮助了。例如我们常用的MAT工具,可以较方便的让我们定位程序中内存的使用情况,是哪块导致了内存的泄露等。

    但由于传统的分析过程比较麻烦,需要使用Jdkjmap(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.       利用反射调用VirtualMachineattach方法,获取VirtualMachine的实例对象

4.       复用反射调用VirtualMachine实例的heapHisto方法,参数为 –all, 可获到JVM的堆内存信息

5.       最后解析heapHisto方法返回的输入流,读取内存数据即可获得当前JVM的堆内存数据

下面帖出的主要代码来说明各步骤具体实现方法:

JDK6中在tools.jar类库里有一个com.sun.tools.attach.VirtualMachine类,该类可以获得JVM虚拟机的相关控制权限。

    private static Class<?> findVirtualMachineClass() throwsClassNotFoundException,

           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);

       }

}

 

利用反射调用VirtualMachineattach方法,获取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;

}

 

复用反射调用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" } });

 

 

最后解析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 阅读(1715) 评论(6)  编辑  收藏 所属分类: Best Practise(JDK API)
分享到:
评论

相关推荐

    java核心面试技术点

    堆内存:存放对象:用来存放由new创建的对象和数组;特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中...

    java核心面试

    堆内存:存放对象:用来存放由new创建的对象和数组;特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。 java 内存模型 ( java memory model ):根据Java Language Specification中的说明, jvm系统中...

    疯狂JAVA讲义

    学生提问:为什么有栈内存和堆内存之分? 93 4.6.2 基本类型数组的初始化 95 4.6.3 引用类型数组的初始化 96 4.6.4 没有多维数组 99 学生提问:我是否可以让图4.13中灰色覆盖的数组元素再次指向另一个数组?这样...

    Java虚拟机

    2.4.1 Java堆溢出 2.4.2 虚拟机栈和本地方法栈溢出 2.4.3 方法区和运行时常量池溢出 2.4.4 本机直接内存溢出 2.5 本章小结 第3章 垃圾收集器与内存分配策略 3.1 概述 3.2 对象已死吗 3.2.1 引用计数算法 ...

    java 面试题 总结

    但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    第2章 Java内存区域与内存溢出异常 / 24 2.1 概述 / 24 2.2 运行时数据区域 / 25 2.2.1 程序计数器 / 25 2.2.2 Java虚拟机栈 / 26 2.2.3 本地方法栈 / 27 2.2.4 Java堆 / 27 2.2.5 方法区 / 28 2.2.6 运行...

    超级有影响力霸气的Java面试题大全文档

    但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地...

    JAVA面试题最全集

    7.Java多态的实现(继承、重载、覆盖) 8.编码转换,怎样实现将GB2312编码的字符串转换为ISO-8859-1编码的字符串。 9.Java中访问数据库的步骤,Statement和PreparedStatement之间的区别。 10.找出下列代码可能...

    java面试题

    65. 什么是java序列化,如何实现java序列化? 48 65.1. java序列化、反序列化 48 65.2. 对象的序列化主要有两种用途: 48 65.3. 对象序列化包括如下步骤: 49 65.4. 对象反序列化的步骤如下: 49 66. 反射机制 49 ...

    操作系统(内存管理)

    从可用的内存中获取一部分内存。 向可用内存池(pool)中返回部分内存,以使其可以由程序的其他部分或者其他程序使用。 实现这些需求的程序库称为 分配程序(allocators),因为它们负责分配和回收内存。程序的...

    Java经典入门教程pdf完整版

    Java主要靠Java虚拟机(JⅧM)实现平台无关性 平台无关性就是一次编写,到处运行: Write Once, Run Anywhere 32:分布式 分布式指的是:软件由很多个可以独立执行的模块组成,这些模块被分布在多台计算机 上,可以同时运行,...

    java8stream源码-Oak:用于大数据分析的可扩展并发键值映射

    java8流源码橡木 Oak(堆外分配的键)是一个可扩展的、并发的、内存中的键值 ...从堆外获取键和数据,因此允许使用巨大的堆 (RAM) — 甚至超过 50G — 没有 JVM GC 开销。 为了支持堆外,OakMap 具有嵌入式、高效、基

    IPv4-国家-区域-城市-运营商csv格式数据库-附使用java写的使用demo

    此ip库数据为2018年的,不是最新的,所以不保证准确率。 此IP-COUNTRY-REGION-CITY-LATITUDE-LONGITUDE-ISP-DOMAIN-...如果发现卡住(超过10分钟都没执行完成),请调整堆内存重新执行,通过-Xmx6G -Xmx6G调大内存。

    java8集合源码-oak:用于大数据分析的可扩展并发键值映射

    java8集合源码橡木 ...从堆外获取键和数据,因此允许使用巨大的堆 (RAM) — 甚至超过 50G — 没有 JVM GC 开销。 为了支持堆外,OakMap 具有嵌入式、高效、基于 epoch 的内存管理,主要消除了 JVM GC 开销。 Oa

    java词典源码-the-yoruba-dictionary:约鲁巴语词典的源代码

    java字典源码约鲁巴语字典Web应用程序 Yoruba词典Web应用程序为的后端服务提供支持。 它包括实现各种所需功能的各种模块。 也包含了位于的前端网站的代码。 运行和开发入门 这应该是您5分钟的指南,以使应用程序在...

    Oracle9i的init.ora参数中文说明

    说明: 确定查询是否获取表级的读取锁, 以防止在包含该查询的事务处理被提交之前更新任何对象读取。这种操作模式提供可重复的读取, 并确保在同一事务处理种对相同数据的两次查询看到的是相同的值。 值范围: TRUE | ...

    java核心知识点整理.pdf

    堆(Heap-线程共享)-运行时数据区 ...................................................................................... 23 2.2.5. 方法区/永久代(线程共享) ..................................................

    javasnmp源码-02-jmx-demo:如何使用JMX来管理程序

    举个例子,我们可以通过jconsole监控应用程序的堆内存使用量、线程数、类数,查看某些配置信息,甚至可以动态地修改配置。另外,有时还可以利用JMX来进行测试。 本文将介绍以下内容: 什么是JMX; JMX的基础架构; ...

    java8rt.jar源码-jvm:jvm入门jvm面试题

    java8 rt.jar源码 JVM探究 请你谈谈你对JVM的理解?java8虚拟机和之前的变化和更新? ​ 首先JVM由类装载器,运行时数据区域...由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所

    LargeCollections

    这允许您的集合增长得非常大,因为它不使用 JVM 堆内存。 应该浏览此项目的以获取更多信息。 #关键设计原则# 底层 java.util.Map(java.util.List 和 java.util.Set) 实现由支持。 LevelDB 是一个由 Google 编写的...

Global site tag (gtag.js) - Google Analytics