歡迎您光臨本站 註冊首頁

源碼實現實時獲取Java堆內存信息

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0

  如果大家有遇到過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

  內容太長,只截取了部分

  讀取的是文本內容,而且格式是完全固定的,大家可以直接解析裡面的內容.主要是後面三例,一個是實現的個數,一個是實際的數據內容,一個是實例關聯的類名稱.


[火星人 ] 源碼實現實時獲取Java堆內存信息已經有559次圍觀

http://coctec.com/docs/java/show-post-59794.html