JAVA對象是如何占用內存的


  本文使用的是32位的JVM ,jdk1.6。本文基本是翻譯的,加上了一些自己的理解,原文見文章底下鏈接。
    在本文中,我們討論如何計算或者估計一個JAVA對象占多少內存空間。(注意,使用 Classmexer agent 或者VM insturmentation 可以查詢到一個java對象占用了多少內存。)
    一般來說,我們討論一個在堆中的對象的內存,前提是在“正常狀態”下。我們忽略下面兩種情況。
  •     在某些情況下,JVM 不一定會把對象放到堆中。例如,一個簡單的線程本地對象可以存放在棧中。
  •     一個對象占用的內存還依賴於它的當前狀態:例如,這個對象的同步鎖是否是處於競爭狀態,或者 這個對象是否正在被GC回收。
 
計算消耗內存的一般方法
 
一般來說,在Hotspot中,一個JAVA對象的內存消耗包括以下四個方面:

 

  1.     對象頭,包括一些對象的狀態。一個實例對象在堆中的內存消耗不僅僅花在它的屬性上,這個對象還需要一些額外的信息,例如,保存一個對象的類引用和判斷這個對象是否可以到達的,當前同步鎖的情況的狀態標記符等。

     

      在HotSpot中:(其他的JVM的情況也跟以下描述差不多。)

    •         一個普通的對象需要8個字節的額外內存空間
    •         一個數組需要12個字節(普通對象頭的8個字節+4個)
  2.     基本類型的內存消耗,如int,long,float
  3.     引用屬性的內存消耗,每個引用消耗4個字節。
  4.     填充,一個對象可能有一小部分的消耗浪費在填充上面。在HotSpot中,給對象分配內存最小單位是8個字節所以每個對象的占的字節數都是可以被8整除的。如果一個對象所占的字節數不是8的倍數,那么向上取最接近的可以被8整除的數字。
 

 

例如:
    一個空的實例對象占8個字節。
    一個包含一個  boolean  屬性的對象占用16個字節內存:對象頭占了8個, boolean 屬性占了1個,8+1=9字節,因為9不是8的倍數,向上取最小可以被8整除的數字16。
    一個包含了8個  boolean 屬性的對象也是占用16個字節內存:對象頭8個, boolean 屬性占了1*8個,一共16個,16可以被8整除,不需要填充字節。
    一個包含了兩個long屬性,3個 int屬性和一個 boolean的對象占用了40個字節內存:
  •             對象頭占用了8個字節
  •             2個long占用了16字節
  •             3個int占用了12個字節
  •             1個boolean占用了1個字節
  •             填充字節占用了3個字節。前面一共占用了37個字節,37不能被8整除,取40字節。
 
怎么去計算一個JAVA數組使用的內存
 
    前面寫到了怎么計算一個JAVA對象的內存消耗,下面我們討論下特殊情況數組。我們知道:
  •     在JAVA中,數組是一種特殊類型的對象
  •     一個多維數組是一個簡單數組的數組
    例如,一個二維數組的每一行都是一個獨立的數組對象
 
單個數組的內存使用情況
    一個一維數組是一個對象,數組也有對象頭,可是,這個對象頭占用了12個字節,額外的4個字節用來存儲數組的長度。具體每個元素需要多少字節,取決於元素的類型。每一個元素需要4個字節來存儲它的引用。並且如果總的字節數不是8個倍數,同樣需要填充字節。
 
二維數組的占用內存情況
    在c語言中,二維數組(事實上是任何多維數組)本質上是一維數組通過指針操作來實現的。但是JAVA中並不是這樣,JAVA中多維數組事實上是一系列的內嵌數組。這說明二維數組的每一行都有和對象一樣的內存開銷,因為他本質上就是一個獨立的對象。
    例如,有一個10×10的 int數組,首先,“外部”數組有12個字節的對象頭開銷。然后有十個元素,每個元素存儲了一個指向10個元素數組的引用。以上的開銷是12+4*10=52字節。然后每一行中都有一個數組對象,一個數組對象的對象頭開銷為12字節,並且有10個 int.開銷10*4 = 40 字節,另外還有4個填充字節,所以每一行的數組對象占用56個字節。所以一共有56*10+52 = 612個字節,加上4個填充字節一共616個字節。所以,得出的字節總數會比如果只是考慮10*10*4=400 字節要多。 多維數組和二維數組同理可得。
 
                                
 
 
JAVA String和 String 相關的對象的內存消耗
 
    我們的應用基本都會用到字符串,如果你用到C語言,並且使用ASCII編碼,字符串由數字,字母或者某些特殊符號組成。那么每個字符串的大小就是字符數目*1字節。但是JAVA不是這樣的:
    首先,每個對象都包含對象頭,所以一個對象最低占用8個字節。
  •     一個JAVA String 包含不止一個對象
  •     一個JAVA char占用兩個字節。
  •     一個JAVA對象包含一些額外的變量。
 
怎么計算字符串內存的使用情況
    首先一個占用最少字節的 String,可以用以下公式計算。
8 * (int) ((((no chars) * 2) + 45) / 8)
    或者,用這個方法計算:   
  •     把字符串的字符個數*2個字節
  •     增加38
  •     如果結果不是8的倍數,取比結果大並且最接近的可以被8整除的數。
    這個結果就是 String在堆中最小的占用內存字節數。
 
更進一步
    一般來說,上面的公式可以用於“新建”的 String ,可是,還有另外的情況如下:
  •     如果一個String是另外一個String的子字符串,那么這個String會比上面說到的最小值要大。
  •     一個子字符串可以共用同一個字符數組,所以總體來說,一個父字符串加上幾個子字符的消耗要比用上面公式計算的總和要小。
 
理解String 是怎么占用內存的
 
    來看一個每個 String對象的各個屬性,一個 String包括如下的屬性:
  •         一個char數組(是個獨立的對象用來存儲字符串中的字符)
  •         一個int 的offset屬性(偏移量,用來指出字符串是從char數組中第幾個字符開始的)
  •         一個int 的count屬性(字符串的長度)
  •         最后一個int的hash屬性(用來存儲hashCode的值)
 
    也就是說,即使一個 String不包含任何字符,也需要在數組的引用上面消耗4個字節,再加上3個 int類型的屬性,3*4=12字節,加上對象頭的8個字節,以上一共24個字節(目前還不需要加上填充字節)。然后,一個 char數組的對象頭需要12個字節,加上4個填充字節,一個空的String 消耗了40字節。
    如果 String 包含了17個字符,那么 String 對象本身需要24個字節,但是現在17個字符的 char數組要需12字節 加上 17*2=34字節,12+17*2=46字節,46不是8的倍數,加上填充字節46+2=48字節,那么17個字符的字符串會用到48+24 = 72 字節,可以看到在C語言中占據18個字節的 String 在JAVA中占據了72個字節。
 
子字符串的內存消耗
 
        你可能會感到奇怪為什么字符串會有一個offset的屬性和count屬性。為什么 char數組中的字符直接對應整個字符串呢?是這樣的,當你創建一個子字符串,新創建的子字符串的 char數組其實是指向的父字符串的 char數組的,也就是父子字符串共享一個 char數組。(但是他們有不同的offset和count)。這是一件好事還是壞事依賴於你怎么用:
  •         如果在創建完子字符串之后還需要用到父字符串的話,你可以省了些內存。
  •         如果創建完子字符串之后就不再需要用到父字符串的話,那么就浪費了內存

例如以下的代碼
String str = "Some longish string...";
str.substring(5, 4);

 

    在這代碼中str的char 數組不止是包括了4個字符,它包括了完整的字符串“Some longish string…”,但是str的offset,count屬性改變了。如果這些內存的浪費在應用中是不可以接受的,那么我可以可以重新創建一個新的String :
 
String str = "Some longish string...";
str = new String(str.substring(5,4));
 
  用這種方式創建一個新的Stringchar數組就不會再直接指向"Some longish string…",而是會創建一個適合的4個字符的char 數組對象。
 

資料:

http://www.javamex.com/tutorials/memory/object_memory_usage.shtml 介紹JAVA對象的內存占用
http://www.javamex.com/tutorials/memory/array_memory_usage.shtml  介紹JAVA數組的內存占用
http://www.javamex.com/tutorials/memory/string_memory_usage.shtml 介紹JAVA字符串的內存占用

 


免責聲明!

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



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