對(duì)象內(nèi)存結(jié)構(gòu)
在 HotSpot 虛擬機(jī)中吝镣,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為3塊區(qū)域:
① 對(duì)象頭(Header)
② 實(shí)例數(shù)據(jù)(Instance Data)
③ 對(duì)齊填充 (Padding)
對(duì)象頭(Header)
HotSpot 虛擬機(jī)的對(duì)象頭包括兩部分信息:Mark Word 和 類型指針律罢;如果是數(shù)組對(duì)象的話砍聊,還有第三部分(option)信息:數(shù)組長(zhǎng)度
Mark Word
這部分?jǐn)?shù)據(jù)的長(zhǎng)度在32位和64位的虛擬機(jī)(未開啟壓縮指針)中分別為32bit 和64bit砸捏。Mark Word用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù),如哈希碼(HashCode)勿璃、GC分代年齡飒货、鎖狀態(tài)標(biāo)志、線程持有的鎖廷臼、偏向線程ID苍在、偏向時(shí)間戳等。
對(duì)象頭信息是與對(duì)象定義的數(shù)據(jù)無關(guān)的額外存儲(chǔ)成本荠商,考慮到虛擬機(jī)的空間效率寂恬,Mark Word被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲(chǔ)盡量多的信息,它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間莱没。
存儲(chǔ)內(nèi)容 | 標(biāo)志位 | 狀態(tài) |
---|---|---|
對(duì)象哈希碼初肉、對(duì)象分代年齡 | 01 | 未鎖定 |
指向鎖記錄的指針 | 00 | 輕量級(jí)鎖定 |
指向重量級(jí)鎖的指針 | 10 | 膨脹(重量級(jí)鎖定) |
空,不需要記錄信息 | 11 | GC標(biāo)記 |
偏向線程ID饰躲、偏向時(shí)間戳牙咏、對(duì)象分代年齡 | 01 | 可偏向 |
??標(biāo)志位“01”就被復(fù)用了臼隔,根據(jù)不同的狀態(tài):“未鎖定” or “可偏向” 來確定“01”存儲(chǔ)所表示的內(nèi)容。
類型指針(Class Pointer)
是對(duì)象指向它的類元數(shù)據(jù)的指針妄壶,虛擬機(jī)通過這個(gè)指針來確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例摔握。
數(shù)組長(zhǎng)度(Length)[option]
如果對(duì)象時(shí)一個(gè)Java數(shù)組,那在對(duì)象頭中還必須有一塊用于記錄數(shù)組長(zhǎng)度的數(shù)據(jù)丁寄。因?yàn)樘摂M機(jī)可以通過普通Java對(duì)象的元數(shù)據(jù)信息確定Java對(duì)象的大小氨淌,但是從數(shù)組的元數(shù)據(jù)中無法確定數(shù)組的大小。
實(shí)例數(shù)據(jù)(Instance Data)
實(shí)例數(shù)據(jù)部分是對(duì)象真正存儲(chǔ)的有效信息伊磺,也是在程序代碼中所定義的各種類型的字段內(nèi)容盛正,無論是從父類繼承下來的,還是在子類中定義的屑埋,都需要記錄起來豪筝。
HotSpot虛擬機(jī)默認(rèn)的分配策略為longs/doubles、ints摘能、shorts/chars续崖、bytes/booleans、oops(Ordinary Object Pointers)徊哑,從分配策略中可以看出袜刷,相同寬度的字段總是被分配到一起。在滿足這個(gè)前提條件的情況下莺丑,在父類中定義的變量會(huì)出現(xiàn)在子類之前著蟹。如果CompactFields參數(shù)值為true(默認(rèn)為true),那子類之中較窄的變量也可能會(huì)插入到父類變量的空隙之中梢莽。
對(duì)齊填充 (Padding)
對(duì)齊填充并不是必然存在的萧豆,也沒有特別的含義,它僅僅起著占位符的作用昏名。由于HotSpot VM的自動(dòng)內(nèi)存管理系統(tǒng)要求對(duì)象起始地址必須是8字節(jié)的整數(shù)倍涮雷,換句話說就是對(duì)象的大小必須是8字節(jié)的整數(shù)倍。
對(duì)象占用內(nèi)存大小
上面我們已經(jīng)對(duì)對(duì)象在內(nèi)存的布局有了一點(diǎn)你的了解轻局,接下來我們來看看對(duì)象占用內(nèi)存的大小洪鸭。也就是對(duì)象內(nèi)存結(jié)構(gòu)的每個(gè)部分分別占用多少的內(nèi)存。
對(duì)象頭
普通對(duì)象占用內(nèi)存情況:
? | 32 位系統(tǒng) | 64 位系統(tǒng)(+UseCompressedOops) | 64 位系統(tǒng)(-UseCompressedOops) |
---|---|---|---|
Mark Word | 4 bytes | 8 bytes | 8 bytes |
Class Pointer | 4 bytes | 4 bytes | 8 bytes |
對(duì)象頭 | 8 bytes | 12 bytes | 16 bytes |
數(shù)組對(duì)象占用內(nèi)存情況:
? | 32 位系統(tǒng) | 64 位系統(tǒng)(+UseCompressedOops) | 64 位系統(tǒng)(-UseCompressedOops) |
---|---|---|---|
Mark Word | 4 bytes | 8 bytes | 8 bytes |
Class Pointer | 4 bytes | 4 bytes | 8 bytes |
Length | 4 bytes | 4 bytes | 4 bytes |
對(duì)象頭 | 12 bytes | 16 bytes | 20 bytes |
實(shí)例數(shù)據(jù)
Type | 32 位系統(tǒng) | 64 位系統(tǒng)(+UseCompressedOops) | 64 位系統(tǒng)(-UseCompressedOops) |
---|---|---|---|
double | 8 bytes | 8 bytes | 8 bytes |
long | 8 bytes | 8 bytes | 8 bytes |
float | 4 bytes | 4 bytes | 4 bytes |
int | 4 bytes | 4 bytes | 4 bytes |
char | 2 bytes | 2 bytes | 2 bytes |
short | 2 bytes | 2 bytes | 2 bytes |
byte | 1 bytes | 1 bytes | 1 bytes |
boolean | 1 bytes | 1 bytes | 1 bytes |
oops(ordinary object pointers) | 4 bytes | 4 bytes | 8 bytes |
實(shí)例分析
環(huán)境
系統(tǒng):macOS 10.12.5
JDK:jdk1.8.0_144
涉及JVM參數(shù)
-XX:+UseCompressedOops(JDK 8下默認(rèn)為啟用)
UseCompressedOops
Use 32-bit object references in 64-bit VM. lp64_product means flag is always constant in 32 bit VM
在64位系統(tǒng)中使用32位系統(tǒng)下引用的大小仑扑,也就是說览爵,在64系統(tǒng)下回壓縮普通對(duì)象的指針大小以節(jié)約內(nèi)存占用的大小。
-XX:+CompactFields(JDK 8下默認(rèn)為啟用)
CompactFields
Allocate nonstatic fields in gaps between previous fields
分配一個(gè)非static的字段在前面字段縫隙中镇饮。這么做也是為了提高內(nèi)存的利用率蜓竹。
-XX:FieldsAllocationStyle=1 (JDK 8下默認(rèn)值為‘1’)
FieldsAllocationStyle
0 - type based with oops first, 1 - with oops last, 2 - oops in super and sub classes are together
實(shí)例對(duì)象中有效信息的存儲(chǔ)順序:
0:先放入oops(普通對(duì)象引用指針),然后在放入基本變量類型(順序:longs/doubles、ints俱济、shorts/chars嘶是、bytes/booleans)
1:先放入基本變量類型(順序:longs/doubles、ints蛛碌、shorts/chars聂喇、bytes/booleans),然后放入oops(普通對(duì)象引用指針)
2:oops和基本變量類型交叉存儲(chǔ)
關(guān)于上面的JVM選項(xiàng)含義左医,可以結(jié)合下面的實(shí)例分析授帕,更便于理解同木。
實(shí)例
下文中無特殊說明浮梢,“對(duì)象占用內(nèi)存大小”均指“對(duì)象自身占用內(nèi)存大小”
實(shí)例一
/**
* ① 將下載的 classmexer.jar 加入當(dāng)前項(xiàng)目的classpath中
* ② 啟動(dòng)Main是添加啟動(dòng)項(xiàng):-javaagent:${classmexer_path}/classmexer.jar
* -javaagent:/Users/linling/Documents/software/classmexer-0_03/classmexer.jar
*
* ③ JVM 參數(shù):
* -XX:+UseCompressedOops (默認(rèn)啟用)
* -XX:+CompactFields (默認(rèn)啟用)
* -XX:FieldsAllocationStyle=1 (默認(rèn)為1)
*/
public class TheObjectMemory {
private static Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* ● 對(duì)象頭:mark word(8 bytes) + class pointer(4 bytes) = 12 bytes
* 因?yàn)樵贘DK 8 中"UseCompressedOops"選項(xiàng)是默認(rèn)啟用的,因此class pointer只占用了4個(gè)字節(jié)彤路。
* 同時(shí)秕硝,從屬性'a'在內(nèi)存中的偏移量為12也能說明,對(duì)象頭僅占用了12bytes(屬性a的分配緊跟在對(duì)象頭后)
*
* ● 實(shí)例數(shù)據(jù):int (4 bytes)
*
* ● 對(duì)齊填充:0 bytes
* 因?yàn)?對(duì)象頭' + '對(duì)齊填充' 已經(jīng)滿足為8的倍數(shù)洲尊,因此無需填充
*
* 對(duì)象占用內(nèi)存大性恫颉:對(duì)象頭(12) + 實(shí)例數(shù)據(jù)(4) + 對(duì)齊填充(0) = 16
*/
int a;
public static void main(String[] args) throws NoSuchFieldException {
TheObjectMemory obj = new TheObjectMemory();
// memoryUsage : 16
System.out.println("memoryUsage : " + MemoryUtil.memoryUsageOf(obj));
// a field offset : 12
System.out.println("a field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("a")));
}
}
實(shí)例二
/**
* ① 將下載的 classmexer.jar 加入當(dāng)前項(xiàng)目的classpath中
* ② 啟動(dòng)Main是添加啟動(dòng)項(xiàng):-javaagent:${classmexer_path}/classmexer.jar
* -javaagent:/Users/linling/Documents/software/classmexer-0_03/classmexer.jar
*
* ③ JVM 參數(shù):
* -XX:+UseCompressedOops (默認(rèn)啟用)
* -XX:+CompactFields (默認(rèn)啟用)
* -XX:FieldsAllocationStyle=1 (默認(rèn)為1)
*/
public class TheObjectMemory {
private static Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* ● 對(duì)象頭:mark word(8 bytes) + class pointer(4 bytes) = 12 bytes
* 因?yàn)樵贘DK 8 中"UseCompressedOops"選項(xiàng)是默認(rèn)啟用的,因此class pointer只占用了4個(gè)字節(jié)坞嘀。
*
* ● 實(shí)例數(shù)據(jù):long (8 bytes) + long (8 bytes)
*
* ● 對(duì)齊填充:4 bytes
*
* 對(duì)象占用內(nèi)存大星ぁ:對(duì)象頭(12) + 實(shí)例數(shù)據(jù)(16) + 對(duì)齊填充(4) = 32
* 這里請(qǐng)注意,padding的填充不是在最后面的丽涩,即棺滞,不是在實(shí)例數(shù)據(jù)分配完后填充了4個(gè)字
* 節(jié)。而是在對(duì)象頭分配完后填充了4個(gè)字節(jié)矢渊。這從屬性'a'字段的偏移量為16继准,也能夠說明填充的部分是對(duì)象頭后的4個(gè)字節(jié)空間。
*
* 這是為什么了矮男?
* 是這樣的移必,在64位系統(tǒng)中,CPU一次讀操作可讀取64bit(8 bytes)的數(shù)據(jù)毡鉴。如果崔泵,你在對(duì)象頭分配后就進(jìn)行屬性 long a字
* 段的分配,也就是說從偏移量為12的地方分配8個(gè)字節(jié)猪瞬,這將導(dǎo)致讀取屬性long a時(shí)需要執(zhí)行兩次讀數(shù)據(jù)操作憎瘸。因?yàn)榈谝淮巫x取
* 到的數(shù)據(jù)中前4字節(jié)是對(duì)象頭的內(nèi)存,后4字節(jié)是屬性long a的高4位(Java 是大端模式)撑螺,低4位的數(shù)據(jù)則需要通過第二次讀取
* 操作獲得含思。
*/
long a;
long b;
public static void main(String[] args) throws NoSuchFieldException {
TheObjectMemory obj = new TheObjectMemory();
// memoryUsage : 32
System.out.println("memoryUsage : " + MemoryUtil.memoryUsageOf(obj));
// a field offset : 16
System.out.println("a field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("a")));
// b field offset : 24
System.out.println("b field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("b")));
}
}
實(shí)例三
/**
* ① 將下載的 classmexer.jar 加入當(dāng)前項(xiàng)目的classpath中
* ② 啟動(dòng)Main是添加啟動(dòng)項(xiàng):-javaagent:${classmexer_path}/classmexer.jar
* -javaagent:/Users/linling/Documents/software/classmexer-0_03/classmexer.jar
*
* ③ JVM 參數(shù):
* -XX:+UseCompressedOops (默認(rèn)啟用)
* -XX:+CompactFields (默認(rèn)啟用)
* -XX:FieldsAllocationStyle=1 (默認(rèn)為1)
*/
public class TheObjectMemory {
private static Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* ● 對(duì)象頭:mark word(8 bytes) + class pointer(4 bytes) = 12 bytes
* 因?yàn)樵贘DK 8 中"UseCompressedOops"選項(xiàng)是默認(rèn)啟用的,因此class pointer只占用了4個(gè)字節(jié)。
*
* ● 實(shí)例數(shù)據(jù):long (8 bytes) + int (4 bytes)
*
* ● 對(duì)齊填充:0 bytes
*
* 對(duì)象占用內(nèi)存大泻恕:對(duì)象頭(12) + 實(shí)例數(shù)據(jù)(12) + 對(duì)齊填充(0) = 24
*
* 在前面的理論中饲做,我們說過基本變量類型在內(nèi)存中的存放順序是從大到小的(順序:longs/doubles、ints遏弱、
* shorts/chars盆均、bytes/booleans)。所以漱逸,按理來說泪姨,屬性int b應(yīng)該被分配到了屬性long a的后面。但是饰抒,從屬性位置
* 偏移量的結(jié)果來看肮砾,我們卻發(fā)現(xiàn)屬性int b被分配到了屬性long a的前面,這是為什么了袋坑?
* 是這樣的仗处,因?yàn)镴VM啟用了'CompactFields'選項(xiàng),該選項(xiàng)運(yùn)行分配的非靜態(tài)(non-static)字段被插入到前面字段的空隙
* 中枣宫,以提供內(nèi)存的利用率婆誓。
* 從前面的實(shí)例中,我們已經(jīng)知道也颤,對(duì)象頭占用了12個(gè)字節(jié)洋幻,并且再次之后分配的long類型字段不會(huì)緊跟在對(duì)象頭后面分配,而是
* 在新一個(gè)8字節(jié)偏移量位置處開始分配翅娶,因此對(duì)象頭和屬性long a直接存在了4字節(jié)的空隙文留,而這個(gè)4字節(jié)空隙的大小符合(即,
* 大小足以用于)屬性int b的內(nèi)存分配故觅。所以厂庇,屬性int b就被插入到了對(duì)象頭與屬性long a之間了。
*/
long a;
int b;
public static void main(String[] args) throws NoSuchFieldException {
TheObjectMemory obj = new TheObjectMemory();
// memoryUsage : 24
System.out.println("memoryUsage : " + MemoryUtil.memoryUsageOf(obj));
// a field offset : 16
System.out.println("a field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("a")));
// b field offset : 12
System.out.println("b field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("b")));
}
}
實(shí)例四
/**
* ① 將下載的 classmexer.jar 加入當(dāng)前項(xiàng)目的classpath中
* ② 啟動(dòng)Main是添加啟動(dòng)項(xiàng):-javaagent:${classmexer_path}/classmexer.jar
* -javaagent:/Users/linling/Documents/software/classmexer-0_03/classmexer.jar
*
* ③ JVM 參數(shù):
* -XX:+UseCompressedOops (默認(rèn)啟用)
* -XX:+CompactFields (默認(rèn)啟用)
* -XX:FieldsAllocationStyle=1 (默認(rèn)為1)
*/
public class TheObjectMemory {
private static Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* ● 對(duì)象頭:mark word(8 bytes) + class pointer(4 bytes) = 12 bytes
* 因?yàn)樵贘DK 8 中"UseCompressedOops"選項(xiàng)是默認(rèn)啟用的输吏,因此class pointer只占用了4個(gè)字節(jié)权旷。
*
* ● 實(shí)例數(shù)據(jù):long (8 bytes) + int (4 bytes) + oops (4 bytes)
*
* ● 對(duì)齊填充:4 bytes
*
* 對(duì)象占用內(nèi)存大小:對(duì)象頭(12) + 實(shí)例數(shù)據(jù)(16) + 對(duì)齊填充(4) = 32
*
* 從屬性 int a贯溅、long b拄氯,以及對(duì)象引用 str 的偏移量可以發(fā)現(xiàn),對(duì)象引用是在基本變量分配完后才進(jìn)行的分配的它浅。這是通過
* JVM選項(xiàng)'FieldsAllocationStyle=1'決定的译柏,F(xiàn)ieldsAllocationStyle的值為1,說明:先放入基本變量類型(順序:
* longs/doubles姐霍、ints鄙麦、shorts/chars典唇、bytes/booleans),然后放入oops(普通對(duì)象引用指針)
*
*/
int a;
long b;
String str;
public static void main(String[] args) throws NoSuchFieldException {
TheObjectMemory obj = new TheObjectMemory();
// memoryUsage : 24
System.out.println("memoryUsage : " + MemoryUtil.memoryUsageOf(obj));
// a field offset : 12
System.out.println("a field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("a")));
// str field offset : 16
System.out.println("b field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("b")));
// str field offset : 24
System.out.println("str field offset : " +
UNSAFE.objectFieldOffset(TheObjectMemory.class.getDeclaredField("str")));
}
}
實(shí)例五
/**
* ① 將下載的 classmexer.jar 加入當(dāng)前項(xiàng)目的classpath中
* ② 啟動(dòng)Main是添加啟動(dòng)項(xiàng):-javaagent:${classmexer_path}/classmexer.jar
* -javaagent:/Users/linling/Documents/software/classmexer-0_03/classmexer.jar
*
* ③ JVM 參數(shù):
* -XX:+UseCompressedOops (默認(rèn)啟用)
* -XX:+CompactFields (默認(rèn)啟用)
* -XX:FieldsAllocationStyle=1 (默認(rèn)為1)
*/
public class TheObjectMemory {
private static Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* memoryUsageOf方法僅計(jì)算了對(duì)象本身的大小胯府,并未包含引用對(duì)象的內(nèi)存大薪橄巍(注意,memoryUsageOf方法計(jì)算的是引用指針
* 的對(duì)象骂因,而非引用對(duì)象占用的內(nèi)存大醒卓А)。
* deepMemoryUsageOf方法則會(huì)將引用對(duì)象占用的內(nèi)存大小也計(jì)算進(jìn)來寒波。
*
* 注意乘盼,deepMemoryUsageOf(Object obj)默認(rèn)只會(huì)包含non-public的引用對(duì)象的大
* 小。如果你想將public引用對(duì)象的大小也計(jì)算在內(nèi)俄烁,可通過deepMemoryUsageOf重載方法
* deepMemoryUsageOf(Object obj, VisibilityFilter referenceFilter)绸栅,VisibilityFilter參數(shù)傳入
* 'VisibilityFilter.ALL'來實(shí)現(xiàn)。
*/
static class TheInnerObject {
int innerA;
}
TheInnerObject innerObject = new TheInnerObject();
public static void main(String[] args) throws NoSuchFieldException {
TheObjectMemory obj = new TheObjectMemory();
// TheObjectMemory memoryUsage : 16
System.out.println("TheObjectMemory memoryUsage : " + MemoryUtil.memoryUsageOf(obj));
// TheInnerObject memoryUsage : 16
TheInnerObject innerObj = new TheInnerObject();
System.out.println("TheInnerObject memoryUsage : " + MemoryUtil.memoryUsageOf(innerObj));
// TheObjectMemory deepMemoryUsageOf : 32
System.out.println("TheObjectMemory deepMemoryUsageOf : " +
MemoryUtil.deepMemoryUsageOf(obj));
}
}
實(shí)例六
/**
* ① 將下載的 classmexer.jar 加入當(dāng)前項(xiàng)目的classpath中
* ② 啟動(dòng)Main是添加啟動(dòng)項(xiàng):-javaagent:${classmexer_path}/classmexer.jar
* -javaagent:/Users/linling/Documents/software/classmexer-0_03/classmexer.jar
*
* ③ JVM 參數(shù):
* -XX:+UseCompressedOops (默認(rèn)啟用)
* -XX:+CompactFields (默認(rèn)啟用)
* -XX:FieldsAllocationStyle=1 (默認(rèn)為1)
*/
public class TheObjectMemory {
private static Unsafe UNSAFE;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 數(shù)組對(duì)象自身占用的內(nèi)存大小 = 對(duì)象頭 + 數(shù)組長(zhǎng)度 * 元素引用指針/基本數(shù)據(jù)類型大小 + 對(duì)齊填充
*
* ● 對(duì)象頭:mark word(8 bytes) + class pointer(4 bytes) + length(4 bytes) = 16 bytes
* 因?yàn)樵贘DK 8 中"UseCompressedOops"選項(xiàng)是默認(rèn)啟用的猴娩,因此class pointer只占用了4個(gè)字節(jié)阴幌。
*
* ● 實(shí)例數(shù)據(jù):數(shù)組長(zhǎng)度(1) * 對(duì)象引用指針(4 bytes) = 4 bytes
*
* ● 對(duì)齊填充:4 bytes
*
* 對(duì)象占用內(nèi)存大猩撞:對(duì)象頭(16) + 實(shí)例數(shù)據(jù)(4) + 對(duì)齊填充(4) = 24
*
* deepMemoryUsageOf = array memoryUsage + array_length(數(shù)組長(zhǎng)度) * item_deepMemoryUsage (元素占用
* 的全部?jī)?nèi)存)
*
* 注意卷中,這里的數(shù)組是一個(gè)對(duì)象數(shù)組,因此memoryUsage中計(jì)算的是對(duì)象引用指針的大小渊抽。如果是一個(gè)基本數(shù)據(jù)類型的數(shù)組蟆豫,如,
* int[]懒闷,則十减,memoryUsage計(jì)算的就是基本數(shù)據(jù)類型的大小了。也就是說愤估,如果是基本數(shù)據(jù)類型的數(shù)組的話帮辟,memoryUsage
* 的值是等于deepMemoryUsageOf的值的。
*
*/
int a;
String str = "hello";
public static void main(String[] args) throws NoSuchFieldException {
TheObjectMemory[] objArray = new TheObjectMemory[1];
TheObjectMemory obj = new TheObjectMemory();
objArray[0] = obj;
// memoryUsage : 24
System.out.println("objArray memoryUsage : " + MemoryUtil.memoryUsageOf(objArray));
// deepMemoryUsageOf : 104
System.out.println("objArray deepMemoryUsageOf : " + MemoryUtil.deepMemoryUsageOf(objArray));
// obj memoryUsage : 24
System.out.println("obj memoryUsage : " + MemoryUtil.memoryUsageOf(obj));
// obj deepMemoryUsageOf : 80
System.out.println("obj deepMemoryUsageOf : " + MemoryUtil.deepMemoryUsageOf(obj));
// first item offset(數(shù)組第一個(gè)元素的內(nèi)存地址偏移量) : 16
System.out.println("first item offset : " + UNSAFE.arrayBaseOffset(objArray.getClass()));
}
}
后記
如果文章有錯(cuò)不吝指教 :)
參考
《深入理解Java虛擬機(jī)》
classmexer
object_memory_usage
jvm-options