1.對象的建立java
①虛擬機遇到一條new指令是,首先將去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,而且檢查這個符號引用表明的類是否已被加載、解析和初始化過。若是沒有必須先加載類。(後續再補充)數組
②接下來虛擬機將爲新生對象分配內存。對象所需內存的大小在類加載完成後即可徹底肯定。爲對象分配內存的方式能夠分爲在連續內存空間上的的「指針碰撞」方式和在不連續內存空間的「空閒列表」方式。分配對象內存空間時即便僅僅修改指針所指向的位置,在併發狀況下也不是線程安全的,兩種解決方案:一種對分配內存空間的動做進行同步處理——實際上虛擬機採用CAS配上失敗重試的方式保證更新操做的原子性;另外一種是把內存分配的動做按照線程劃分在不一樣的空間之中進行,即每一個線程在java堆中預先分配一小塊內存,稱爲本地線程分配緩衝(Thread Local Allocation Buffer,TLAB)。安全
③內存分配完成後,虛擬機須要將分配到的內存空間都初始化爲零值(不包括頭對象)。若是使用TLAB能夠提早至分配TLAB時進行數據結構
④接下來虛擬機要對對象進行必要的設置,例如對象是哪一個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡等信息。這些信息放在對象頭中。併發
2.對象的內存佈局佈局
對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。spa
HotSpot虛擬機的對象頭包括兩部分信息,第一部分用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等等,這部分數據的長度在32位和64位的虛擬機(暫不考慮開啓壓縮指針的場景)中分別爲32個和64個Bits,官方稱它爲「Mark Word」。對象須要存儲的運行時數據不少,其實已經超出了3二、64位Bitmap結構所能記錄的限度,可是對象頭信息是與對象自身定義的數據無關的額外存儲成本,考慮到虛擬機的空間效率,Mark Word被設計成一個非固定的數據結構以便在極小的空間內存儲儘可能多的信息,它會根據對象的狀態複用本身的存儲空間。例如在32位的HotSpot虛擬機中對象未被鎖定的狀態下,Mark Word的32個Bits空間中的25Bits用於存儲對象哈希碼(HashCode),4Bits用於存儲對象分代年齡,2Bits用於存儲鎖標誌位,1Bit固定爲0,在其餘狀態(輕量級鎖定、重量級鎖定、GC標記、可偏向)下對象的存儲內容以下表所示。線程
存儲內容設計 |
標誌位指針 |
狀態 |
對象哈希碼、對象分代年齡 |
01 |
未鎖定 |
指向鎖記錄的指針 |
00 |
輕量級鎖定 |
指向重量級鎖的指針 |
10 |
膨脹(重量級鎖定) |
空,不須要記錄信息 |
11 |
GC標記 |
偏向線程ID、偏向時間戳、對象分代年齡 |
01 |
可偏向 |
對象頭的另一部分是類型指針,便是對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例。並非全部的虛擬機實現都必須在對象數據上保留類型指針,換句話說查找對象的元數據信息並不必定要通過對象自己。另外,若是對象是一個Java數組,那在對象頭中還必須有一塊用於記錄數組長度的數據,由於虛擬機能夠經過普通Java對象的元數據信息肯定Java對象的大小,可是從數組的元數據中沒法肯定數組的大小。
3.對象的訪問定位
java程序須要經過棧上的reference數據來操做堆上的具體對象。因爲reference類型在java虛擬機規範中只規定了一個指向對象的引用,並無定義這個引用用何種方式去定位、訪問堆中的對象的具體位置,因此它取決於虛擬機的實現。目前主要有句柄和直接指針兩種。
這兩種方式各有優點,句柄的好處是reference中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是廣泛的行爲)時只會改變句柄中的實例數據指針,而reference自己不須要修改。
使用直接指針的方式好處就是速度更快,它節省了一次指針定位的時間開銷,因爲對象的訪問在java中很是頻繁,所以優點也很大。HotSpot使用直接指針的訪問方式。