一、對象的創建流程
二、對象的內存佈局
在HotSpot(sun jdk和openJDK中所帶的虛擬機,是目前使用最廣的虛擬機)虛擬機中,對象在內存中存儲的佈局可以分爲3塊區域:對象頭(Header)、實例數據(Instance Data)和對齊信息(Padding)。
1、對象頭
對象頭包含兩部分信息
第一部分(Mark Word)
用於存儲對象自身的運行時數據,如哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等。
Mark Word數據的長度在32位和64位的虛擬機(未開啓壓縮指針)中分別爲32bit和64bit
- GC分代年齡
HotSpot使用分代垃圾回收機制,被分爲三個代:年輕代(Young Generation)、年老代(Old Generation)和持久代(Permanent Generation)。
(1)年輕代:所有新生成的對象首先都是放在年輕代的。年輕代的目標就是儘可能快速的收集掉那些生命週期短的對象。
(2)年老代:在年輕代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認爲年老代中存放的
都是一些生命週期較長的對象。
(3)持久代:用於存放靜態文件,如今Java類、方法等。
注意:Java8去除了持久代
- 鎖狀態標誌
Mark Word的最後2bit是鎖狀態標誌位,用來標記當前對象的狀態。對象的所處的狀態決定了Mark Word存儲的內容,如下表所示:
狀態 | 標誌位 | 存儲內容 |
---|---|---|
未鎖定 | 01 | 對象哈希碼、對象分代年齡 |
輕量級鎖定 | 00 | 指向鎖記錄的指針 |
膨脹(重量級鎖定) | 10 | 執行重量級鎖定的指針 |
GC標記 | 11 | 空(不需要記錄信息) |
可偏向 | 01 | 偏向線程ID、偏向時間戳、對象分代年齡 |
第二部分(類型指針)
對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。
2、實例數據
實例數據是對象真正存儲的有效信息,也是在程序代碼中所定義的各種類型的字段內容。
3、對齊填充
不必然存在,也沒有特別的含義,僅僅起了佔位符的作用。HotSpot VM 規定對象的大小必須是8字節的整數倍,當對象實例數據沒有對齊時,需要對齊填充補全
三、對象的內存分配
Java自動內存管理最終可歸結爲自動化的解決了兩個問題:給對象分配內存及回收分配給對象的內存。
- 對象優先在Eden(新生代Eden區)分配
如果Eden沒有足夠的空間分配,虛擬機將發起一次Minor GC - 大對象直接進入老年代
大對象是指需要大量連續內存空間的Java對象,最典型的大對象就是很長的字符串以及數組。 - 長期存活的對象將進入老年代
每個對象有個年齡計數器。如果對象在Eden出生並經過第一次Minor GC後仍然存活,並且能被Survivor容納的話,將被移動到Survivor空間中,並且對象年齡設爲1.對象在Survivor每熬過一次Minor GC,對象年齡加1。當對象到達15(默認15,通過-XX:MaxTenuringThreshold設置)的時候,將被晉升到老年代。 - 並不是所有對象年齡必須到達MaxTenuringThreshold才能晉升老年代
如果Survivor空間中,相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象可以直接進入老年代。
四、其他
- Minor GC (新生代GC)
發生在新生代的垃圾收集動作,Minor GC非常頻繁,回收速度快。 - Major GC / Full GC (老年代GC)
發生在老年代的GC,出現Major GC,經常會伴隨至少一次的Minor GC(不絕對)。Major GC的速度一般比Minor GC 慢10倍以上。 - Eden 、 Survivor
新生代分爲一個Eden區,兩個Survivor區。Eden和Survivor的空間大小比例是8:1
[1] 周志明 · 深入理解Java虛擬機 :機械工業出版社
[2] GC分代相關概念摘自 https://www.cnblogs.com/weiguo21/p/5195460.html
[3] 鎖狀態標誌相關摘自 https://blog.csdn.net/kirito_j/article/details/79201213