3、JVM中的對象
1、對象的創建
A a = new A()
A:引用的類型
a::引用的名稱
new A():創建一個A類對象
當創建一個對象時,具體創建過程是什麼呢?
(1)JVM遇到new的位元組碼指令後,檢查類是否被載入,否,進行類載入
(2)檢查載入通過後,對新創建的對象在堆中分配記憶體
(3)將分配的記憶體空間進行初始化為0值
(4)設置對象頭的資訊,將對象的所屬類(即類的元數據資訊)、對象的HashCode、對象的GC資訊、鎖資訊等數據存儲在對象頭中
(5)調用對象的構造方法進行初始化
2、對象記憶體的分配策略
對象創建的過程中需要為新對象在堆上劃分出一塊確定大小的記憶體空間,JVM中對於劃分記憶體有兩種策略,指針碰撞和空閑列表
指針碰撞:當記憶體空間絕對規整,使用中的記憶體放一邊,未使用的記憶體放另一邊,中間放由一個作為分界器的指針,當進行記憶體分配時,指針向空閑記憶體方向挪動與對象大小相等的距離
空閑列表:當堆上的記憶體空間不是絕對規整,使用和未使用的記憶體空間呈犬牙交錯的形勢,此時虛擬機需要維護一個列表,列表中記錄了那塊記憶體未被使用,分配記憶體時需要在列表中找到一塊足夠大的記憶體空間或分給新建的對象,並更新表中的記錄。
其中指針碰撞的分配策略性能要更高一些,JVM採用哪種分配策略是由堆上記憶體空間是否絕對規整來決定的,記憶體空間是否絕對規整是由JVM採用哪種GC來決定的
給對象劃分記憶體空間時,不僅要考慮記憶體的分配策略,還需要考慮到記憶體分配時的並發安全,JVM中是怎樣確保記憶體分配時的並發安全呢?
JVM中創建對象十分的頻繁,當對象A創建時,剛為其分配記憶體,還未更新指針或者列表時,對象B來創建,此時就會發生問題
JVM中為了保證並發情況下執行緒安全,採用了兩種方案:CAS失敗重試和分配緩衝
CAS失敗重試:CAS(Compare-and-Swap),即比較並替換,是一種實現並發演算法時常用到的技術,Java並發包(Java.Util.Concurrent)中的原子類都使用了CAS技術。
CAS需要有3個操作數:記憶體地址V,舊的預期值A,即將要更新的目標值B。
CAS指令執行時,當且僅當記憶體地址V的值與預期值A相等時,將記憶體地址V的值修改為B,否則就什麼都不做。整個比較並替換的操作是一個原子操作。
CAS失敗重試流程:一塊空白的記憶體,此時是null值,在空白的記憶體中劃分出一塊與申請對象大小一致的記憶體,劃分完之後,再來看記憶體是否為null,是,為對象分配記憶體成功,否,說明在劃分的過程中有別的執行緒來對這塊記憶體進行了分配的操作,為對象分配記憶體失敗,找到下一塊空白的記憶體,繼續上述操作
分配緩衝:把記憶體分配的動作按照執行緒劃分在不同的空間之中進行,即每個執行緒在 Java 堆中預先分配一小塊私有記憶體,也就是本地執行緒分配緩衝(Thread Local Allocation Buffer,TLAB),JVM 在執行緒初始化時,同時也會申請一塊指定大小的記憶體,只給當前執行緒使用,這樣每個執行緒都單獨擁有一個 Buffer,如果需要分配記憶體,就在自己的 Buffer 上分配,這樣就不存在競爭的情況,可以大大提升分配效率,當 Buffer 容量不夠的時候,再重新從 Eden 區域申請一塊繼續使用。

對象大小為8的整數倍,方便記憶體的劃分
定位對象的方式有兩種:句柄和直接指針
句柄:JVM在堆上劃分出一塊記憶體作為句柄池,引用(reference)中存儲的對象就是句柄的地址,句柄中包含了對象的實例數據與類型數據真實的地址資訊
優點:引用 中存儲的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而引用本身不需要修改
直接指針:引用(reference)中存儲的對象就是真實地址,Sun HotSpot 是使用直接指針訪問方式進行對象訪問的
優點:較比句柄速度要快一些,因為它節省了一次指針定位的時間開銷
6、引用的類型
強引用:一般的 Object obj = new Object() ,就屬於強引用。在任何情況下,只有有強引用關聯(與根可達)還在,垃圾回收器就永遠不會回收掉被引用的對象
軟引用:一些有用但是並非必需,用軟引用關聯的對象,系統將要發生記憶體溢出(OuyOfMemory)之前,這些對象就會被回收(如果這次回收後還是沒有足夠的
弱引用:一些有用(程度比軟引用更低)但是並非必需,用弱引用關聯的對象,只能生存到下一次垃圾回收之前,GC 發生時,不管記憶體夠不夠,都會被回收
虛引用:幽靈引用,最弱(隨時會被回收掉)
7、如何判斷對象是否存活
判斷對象是否存活的方式有兩種:引用計數法和可達性分析
引用計數法:在對象中添加一個引用計數器,每當有一個地方引用它,計數器就加 1,當引用失效時,計數器減 1
這種方法Python中使用,JVM中沒有使用
可達性分析:通過以GC Roots對象為起點,向下搜索,看是否存在引用,搜索走過的路被稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈,說明此對象是無用可被回收的
GC Roots對象:虛擬機棧(棧幀中的本地變數表)中引用的對象
各個執行緒調用方法堆棧中使用到的參數、局部變數、臨時變數等
Finalize方法:即使通過可達性分析判斷不可達的對象,也不是「非死不可」,它還會處於「緩刑」階段,真正要宣告一個對象死亡,需要經過兩次標記過程,一次是沒有找到與 GCRoots 的引用鏈,它將被第一次標記。隨後進行一次篩選(如果對象覆蓋了 finalize),我們可以在 finalize 中去拯救