對象創建方法,對象的內存分配,對象的訪問定位
對象創建方法:
JVM遇到一條new指令時,首先檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被加載、連接和初始化過。
如果沒有,那必須先執行相應的類的加載過程。
對象的內存分配:
對象所需內存的大小在類加載完成後便完全確定(對象內存布局),為對象分配空間的任務等同於把一塊確定大小的內存從Java堆中劃分出來。
根據Java堆中是否規整有兩種內存的分配方式:
指針碰撞:所有用過的內存在一邊,空閑內存在另一邊,中間放著一個指針作為分界點的指示器,
分配內存就是把指針往空閑內存那邊挪一段與對象大小相等的距離。在使用Serial,ParNew等收集器,
(也就是用復制算法,標記-整理算法的收集器),分配算法通常采用指針碰撞。
空閑列表:虛擬機維護一個列表,記錄哪些內存是可用的,分配的時候從列表中找到一塊足夠大的空間劃分給對象,並更新列表。
使用CMS這種基於標記-清除算法的收集器,通常用空閑列表。
對象創建在虛擬機中時非常頻繁的行為,即使是僅僅修改一個指針指向的位置,在並發情況下也並不是線程安全的,可能出現正在給對象A分配內存,指針還沒來得及修改,對象B又同時使用了原來的指針來分配內存的情況。
同步
虛擬機采用CAS配上失敗重試的方式保證更新操作的原子性
本地線程分配緩沖(Thread Local Allocation Buffer, TLAB)
把內存分配的動作按照線程劃分為在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊內存(TLAB)。
哪個線程要分配內存,就在哪個線程的TLAB上分配。只有TLAB用完並分配新的TLAB時,才需要同步鎖定。
內存分配完之後,虛擬機要將分配到的內存空間都初始化為零值(不包括對象頭),保證了對象的實例字段在Java代碼中可以不賦初始值就直接使用。
對象的內存布局:
對象在內存中可分為3個部分,對象頭,實例數據,對齊填充。
對象頭的第一部分用於存儲對象自身的運行時數據,如對象的哈希碼,GC分代年齡,鎖狀態標誌,線程持有的鎖等。
另一部分是類型指針,即對象指向它的類元數據的指針,通過這個來確定這個對象是哪個類的實例。
實例數據是對象真正存儲的有效信息。
對象的訪問定位:
程序要通過棧上的reference數據來操作堆上的具體對象。對象的訪問方式有使用句柄和直接指針。
使用句柄:java堆會劃分一塊內存作為句柄池,reference中存的是對象的句柄地址,而句柄中包含了對象的實例數據的地址和類型數據的地址(在方法區)。
優點:對象被移動,reference不用修改,只會改變句柄中保存的地址。
使用直接指針:reference中存的是對象的地址,對象中分一小塊內存保存類型數據的地址。優點:速度快。
對象創建方法,對象的內存分配,對象的訪問定位