獲取JAVA對象占用的內存大小


  介紹兩種獲取JAVA對象內存大小的方法。

第一種:Instrumentation

簡介:

使用java.lang.instrument 的Instrumentation來獲取一個對象的內存大小。利用Instrumentation並且通過代理我們可以監測在JVM運行的程序的功能,它的原理是修改方法的字節碼。

首先創建代理類
 

 

package com.dingtongblog.size;
import java.lang.instrument.Instrumentation;
 
public class ObjectSize {
    private static volatile Instrumentation instru;
 
    public static void premain(String args, Instrumentation inst) {
        instru = inst;
    }
 
    public static Long getSizeOf(Object object) {
        if (instru == null) {
            throw new IllegalStateException("Instrumentation is null");
        }
        return instru.getObjectSize(object);
    }
}
     premain方法:JVM會首先調用這個方法。通過這個方法我們就可以把屬性instru初始化化成功,通過Instrumentation的getObjectSize(Object object)方法我們就獲取一個對象的大小了。
 

 

然后把這個類打包成jar包
首先我們要創建manifest.txt,並且增加這樣的一行
 
Premain-Class:com.dingtongblog.size.ObjectSize
這個premain-Class指定了是哪個是代理類,也就是包括了premain方法的類。
然后把ObjectSize打包成jar包
 
java -cmf manifest.txt simpleSize.jar com/dingtongblog/size/ObjectSize.class 

運行

然后把jar引入到工程中, 並且啟動參數加入
-javaagent:jarpath[=options]
在命令行中執行
java -javaagent:simpleSize.jar TestMain

(當前TestMain和simpleSize.jar在同一目錄下)

測試代碼如下:

import com.dingtongblog.size.ObjectSize;
public class TestMain {
public static void main(String[] args) {
String a = new String(aa);
System.out.println(ObjectSize.getSizeOf(a));
}
 
}

 

輸出24
 
我們修改參數a
String a = aa; 改成
String a = aaaa;
 
然后再運行main,還是輸出24。
 
    為什么值會沒有改變呢?這里雖然a指向的對象已經改變了,但是輸出的值還是24沒變。這是因為這個使用getSizeOf這個方法取得的對象本身的內存大小,不包含對象中屬性所指向的對象的大小。在 String 中一共有3個 int 屬性,1個數組的引用,再加上對象頭占的字節數為 3 * 4 + 4 +8 = 24 ,然后24正好是8的倍數,不需要填充字節。所以直接輸出24。
 
有辦法可以取到一個對象完整的字節數嗎?有一種思路是通過反射遍歷對象中每個屬性,然后調用上述的方法得到每個對象大小,把得到得對象再重復上面的過程,直到最后指向的是基本類型。直接引用 這篇文章中的jar包。
 
引入包,並且啟動JVM的時候加上參數

 

?
-javaagent:D:\sizeofag.jar

測試代碼:

?
public class TestMain {
  
    public static void main(String[] args) throws IllegalAccessException {
  
        String a = new String(aa);
        System.out.println(SizeOfAgent.fullSizeOf(a));
        System.out.println(SizeOfAgent.sizeOf(a));
  
}

可以看到輸出的結果40,24

 
40就是這個對象的完整大小。首先是前面計算的24字節+數組對象頭的12個字節+兩個字符字節2*2 可以得到24+12+4 =40。40是8個倍數,不需要填充字節。
 
第二種是jmap ,jhat命令
   jmap 可以輸出給定的程序中堆的詳情。
jmap -histo <pid>  (pid為當前JAVA進程的id)

 

例如
jmap -histo 20230

 

這樣就直接輸出當前堆的詳細情況,但是這樣不太直觀。
 
通過

 

jmap -dump:format=b,file=<filename> 

 

可以把java的堆以hprof 二進制格式輸出到一個文件中,然后通過jhat命令來查看,jhat會生成一個頁面,能比較直觀的查看堆詳情。但是jhat需要的內存空間為dump文件的幾倍,如果dump文件比較大會遇到OOM錯誤,這時候可以通過MAT來瀏覽堆信息。

 

例如

jmap -dump:format=b,file=d:\dump.txt

 

然后通過

 

jhat filename ;

  (filename為之前dump出來的文件) 會解析JAVA 堆的DUMP文件並且會啟動一個web服務器,服務器的默認端口為7000,命令執行完之后就可以通過127.0.0.1:7000訪問堆詳情了。

 

jhat d:\dump.txt

 

可以得到類似這樣的頁面。
jhat
 
不過通過這個方法得到的計算結果和之前用的instrumentation方法得到的結果還不一樣。這個地方可能計算的方法有區別,個人覺得instrumentation的結果會比較准,但是還沒找到有關的資料說明這個問題。
另外使用Jprofiler工具也可以監控內存使用的詳細情況。
資料:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM