posts - 156,  comments - 601,  trackbacks - 0

           

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

                 }

          }

           

          利用反射調用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 閱讀(6319) 評論(9)  編輯  收藏 所屬分類: Best Practise(JDK API)
          主站蜘蛛池模板: 青海省| 阜新市| 汝南县| 邵武市| 全南县| 滨州市| 循化| 伊宁县| 金溪县| 资讯 | 田东县| 嘉义市| 于都县| 文昌市| 南江县| 灵丘县| 东港市| 崇阳县| 禄丰县| 博湖县| 大化| 康马县| 凉山| 定西市| 黄骅市| 秀山| 贵阳市| 德兴市| 肇州县| 沙雅县| 墨脱县| 高青县| 太原市| 新津县| 得荣县| 五原县| 郸城县| 芷江| 鄂托克前旗| 庆阳市| 正定县|