關於字節對齊以及內存佔用

 

參考博文: http://www.javamex.com/tutorials/memory/object_memory_usage.shtml

本文主要考慮正常情況下一個對象在堆上的內存佔用情況:對於下面的特殊情況不作討論

1、某些情況下,JVM可能不會把對象存儲在堆上:比如小的線程私有對象原則上會全部存儲在棧或寄存器上,嚴格意義上說並不存在於java堆上

2、對象的內存佔用可能依賴於它當前的狀態,比如說它的同步鎖是否處於競爭狀態、是否正處於垃圾回收階段(這些額外的“系統”數據不一定存儲在java堆上)

在HotSpot虛擬機上,一個java對象的內存佔用一般包括如下幾部分:

1、一個對象頭部信息(包括幾字節的基本元信息)

2、原始類型字段的內存佔用

3、引用字段的內存佔用

4、對齊字節(padding):爲了讓每個對象的開始地址是字節的整數倍,減少對象指針佔用的比特數,對象數據後面會添加一些“無用”的數據(字節),以實現對齊,即保證最終的字節大小是8的倍數

HotSpot虛擬機的對象頭包含兩部分信息:1、用於存儲對象自身的運行時數據,這部分數據在32位和64位的虛擬機(未開啓壓縮指針)中分別爲32bit和64bit。

5、類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。注:如果java對象是一個數組,還必須包含用於記錄數組長度的數據,因爲java虛擬機可以從普通java對象的元數據信息確定對象的大小,但是從數組的元數據中卻無法確定數組的大小。

下圖描述了32bit下對象頭的存儲狀態:

實例數據部分是對象真正存儲的有效信息:也即程序代碼定義的各種類型的字段內容。

這部分的存儲順序會受到虛擬機的的分配策略參數和字段在java源碼中定義的順序的影響。

java元數據類型佔用字節列表:

可能會認爲boolean會佔用一比特或者佔用一個字節的第八位,但是HotSpot虛擬機會爲每個Boolean字段分配一個字節的空間。

在HotSpot中,每個對象佔用的內存大小是 8 字節的倍數。如果對象所需的內存大小(包括頭信息和字段)不是 8 的倍數,則會向上取整到 8 的倍數。

也就是說:

1、一個空對象佔用8字節

2、只有一個 boolean 字段的類實例佔 16 字節:頭信息佔 8 字節,boolean 佔 1 字節,爲了對齊達到 8 的倍數會額外佔用 7 個字節

3、包含 8 個 boolean 字段的實例也會佔用 16 字節:頭信息佔用 8 字節,boolean 佔用 8 字節;因爲已經是 8 的倍數,不需要補充額外的數據來對齊

4、一個包含 2 個 long 字段、3 個 int 字段、1 個 boolean 字段的對象將佔用:

  • 頭信息佔 8 字節;
  • 2 個 long 字段佔 16 字節(每個 long 字段佔用 8 字節);
  • 3 個 int 字段佔 12 字節(每個 int 字段佔用 4 字節);
  • 1 個 boolean 字段佔 1 個字節;
  • 爲了對齊額外多 3 個字節(上面加起來是 37 字節,爲滿足對齊 8 的倍數 40)

 關於二維數組佔用字節數計算:注意數組有一個不同的地方在於,它本身會有一個記錄數組長度的int類型,佔用4字節,本身又是一個對象,會佔用8字節

For example, let's consider a 10x10 int array. Firstly, the "outer" array has its 12-byte object header followed by space for the 10 elements. Those elements are object references to the 10 arrays making up the rows. That comes to 12+4*10=52 bytes, which must then be rounded up to the next multiple of 8, giving 56. Then, each of the 10 rows has its own 12-byte object header, 4*10=40 bytes for the actual row of ints, and again, 4 bytes of padding to bring the total for that row to a multiple of 8. So in total, that gives 11*56=616 bytes. That's a bit bigger than if you'd just counted on 10*10*4=400 bytes for the hundred "raw" ints themselves.

關於java內存佔用更爲詳細的描述可以參考廖祜秋大神的博客:http://www.liaohuqiu.net/cn/posts/caculate-object-size-in-java/

廖神的博文中已經指出對於HotSpot,在32位的JVM中,一個對象引用佔用4字節,而在64位的JVM中,一個對象引用佔用8字節(在開啓指針壓縮的話佔用4字節),而在Dalvik中則是始終佔用4字節。

針對Dalvik,元數據類型的大小分別在作爲對象域或變量,以及數組的一個元素時是不同的

在Dalvik中對象對齊邊界也是8字節,但是一個對象的內存佔用和HotSpot是不同的:

會有一個額外的dlmalloc空間佔用,4或8字節

所以一個空對象會佔用16字節(12字節的內存佔用以及4字節的對齊)

示例演示:

class EmptyClass {
}

Total size: 8 (Object overhead) + 4 (dlmalloc) = 12 bytes. For 8 bytes alignment, the final total size is 16 bytes.

class Integer {
    int value;  // 4 bytes
}

The total size is: 8 + 4 + 4 (int) = 16 bytes.

static class HashMapEntry<K, V> {
    final K key;                // 4 bytes
    final int hash;             // 4 bytes
    V value;                    // 4 bytes
    HashMapEntry<K, V> next;    // 4 bytes
}

The total size: 8 + 4 + 4 * 4 = 28 bytes. Total aligned is 32 bytes.

詳細描述參考廖神博文:http://www.liaohuqiu.net/posts/android-object-size-dalvik/

PS:補充理解

struct A {
   // int a;
    char b;
    short c;
};

struct B {
    char b;
   // int a;
    short c;
};

輸出都是4,說明之前的int影響對齊!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章