posts - 156,  comments - 601,  trackbacks - 0

           

             如果大家有遇到過Java內(nèi)存泄露問題,而且親自動(dòng)手去定位和分析經(jīng)歷的同學(xué)來講,獲取Java的堆內(nèi)信息對(duì)了內(nèi)存使用情況的問題分析和定位是非常有幫助了。例如我們常用的MAT工具,可以較方便的讓我們定位程序中內(nèi)存的使用情況,是哪塊導(dǎo)致了內(nèi)存的泄露等。

              但由于傳統(tǒng)的分析過程比較麻煩,需要使用Jdkjmap(Java Memory Map)命令把heap內(nèi)存dump到一個(gè)文件,然后用MAT進(jìn)行分析。所以本文介紹一種方法可以實(shí)現(xiàn)在線查看heap內(nèi)存的使用情況,并附上源碼實(shí)現(xiàn),希望對(duì)大家有幫助。由于目前調(diào)研中只找到了Sun JDK6以及以上版本的實(shí)現(xiàn),所以目前該方案只支持Sun JDK6或以上。如果其他同學(xué)有其它版本的JDK實(shí)現(xiàn)分享,歡迎一起交流。

          整體實(shí)現(xiàn)思路如下:

          1.       JDK6中在tools.jar類庫里有一個(gè)com.sun.tools.attach.VirtualMachine類,該類可以獲得JVM虛擬機(jī)的相關(guān)控制權(quán)限。

          2.       利用getPids.exe或其它工具獲取需要監(jiān)控的JVM pid進(jìn)程號(hào)信息

          3.       利用反射調(diào)用VirtualMachineattach方法,獲取VirtualMachine的實(shí)例對(duì)象

          4.       復(fù)用反射調(diào)用VirtualMachine實(shí)例的heapHisto方法,參數(shù)為 –all, 可獲到JVM的堆內(nèi)存信息

          5.       最后解析heapHisto方法返回的輸入流,讀取內(nèi)存數(shù)據(jù)即可獲得當(dāng)前JVM的堆內(nèi)存數(shù)據(jù)

          下面帖出的主要代碼來說明各步驟具體實(shí)現(xiàn)方法:

          JDK6中在tools.jar類庫里有一個(gè)com.sun.tools.attach.VirtualMachine類,該類可以獲得JVM虛擬機(jī)的相關(guān)控制權(quán)限。

              private static Class<?> findVirtualMachineClass() throws ClassNotFoundException,

                     MalformedURLException {

                 // JVM 虛擬機(jī)操作類

                 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虛擬機(jī)操作類

                     return Class.forName(virtualMachineClassName, true, cl);

                 }

          }

           

          利用反射調(diào)用VirtualMachineattach方法,獲取VirtualMachine的實(shí)例對(duì)象

          本過程相對(duì)比較簡(jiǎn)單,獲取VirtualMachine的類后,根據(jù)反射類,查詢attach方法

          //獲取JVM 虛擬機(jī)操作類后

          final Class<?> virtualMachineClass = findVirtualMachineClass();

          //根據(jù)反射查詢 attach方法,參數(shù)為String類型

          final Method attachMethod = virtualMachineClass.getMethod("attach", String.class);

          //通過 getpids.exe工具獲取當(dāng)前JVM進(jìn)程號(hào)

          final String pid = PID.getPID();

          try {

              //通過反射調(diào)用attache方法

              jvmVirtualMachine = invoke(attachMethod, null, pid);

          } finally {

              enabled = jvmVirtualMachine != null;

          }

           

          復(fù)用反射調(diào)用VirtualMachine實(shí)例的heapHisto方法,參數(shù)為 –all, 可獲到JVM的堆內(nèi)存信息

          本過程也是利用反射調(diào)用heapHisto方法,實(shí)現(xiàn)的代碼如下:

          final Class<?> virtualMachineClass = getJvmVirtualMachine().getClass();

          //反射調(diào)用 heapHisto方法,參數(shù)為 -all

          final Method heapHistoMethod = virtualMachineClass.getMethod("heapHisto",

                 Object[].class);

          //該方面返回值為InputStream

          return (InputStream) invoke(heapHistoMethod, getJvmVirtualMachine(),

                             new Object[] { new Object[] { "-all" } });

           

           

          最后解析heapHisto方法返回的輸入流,讀取內(nèi)存數(shù)據(jù)即可獲得當(dāng)前JVM的堆內(nèi)存數(shù)據(jù), 通過該方法返回的一個(gè)文本內(nèi)容。

          Input Stream取出的結(jié)果(文本內(nèi)容)示例如下:

           

           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

           

          因?yàn)閮?nèi)容太長(zhǎng),只截取了部分

          因?yàn)樽x取的是文本內(nèi)容,而且格式是完全固定的,所以大家可以直接解析里面的內(nèi)容。主要是后面三例,一個(gè)是實(shí)現(xiàn)的個(gè)數(shù),一個(gè)是實(shí)際的數(shù)據(jù)內(nèi)容,最后一個(gè)是實(shí)例關(guān)聯(lián)的類名稱.

           



           Good Luck!
           Yours Matthew!






          posted on 2013-02-20 16:30 x.matthew 閱讀(6331) 評(píng)論(9)  編輯  收藏 所屬分類: Best Practise(JDK API)
          主站蜘蛛池模板: 吉首市| 丰都县| 大关县| 呼玛县| 南安市| 南木林县| 蒲江县| 喀喇沁旗| 胶州市| 车致| 额尔古纳市| 聂荣县| 建昌县| 鄂尔多斯市| 天长市| 嫩江县| 土默特右旗| 富民县| 东安县| 贵州省| 天长市| 丘北县| 洪雅县| 内黄县| 连城县| 乌鲁木齐县| 宝兴县| 平山县| 安岳县| 泰来县| 张家界市| 文登市| 正定县| 尉氏县| 本溪市| 淮北市| 筠连县| 西充县| 洛浦县| 文成县| 信丰县|