- 對象頭存放位置、格式
-
對象頭包含 2 部分,如果有數組則包含第 3 部分記錄數組長度
object header -
Mark Word 根據虛擬機是 32 / 64 位占用不同的長度 [詳見參考.3]
object header demomark word -
輕量鎖-鎖定
- 當前線程的棧幀中建立一個 Lock Record 的空間
- 復制對象頭的 Mark Word 到該空間 (Displaced Mark Word)
- CAS 操作更新對象頭的 Mark Word 為該空間的指針
- CAS 成功則獲取到該對象的鎖,鎖標記為
00
;否則需要競爭,鎖膨脹為重量鎖,鎖標記為10
- Lock Record 跟蹤當前執行方法鎖定的對象(遍歷線程棧找出被鎖對象)
-
輕量鎖-解鎖
- CAS 操作將棧幀中的 Displaced Mark Word 放回對象頭的 Mark Word
- CAS 失敗則出現競爭,說明有其他線程嘗試獲取該鎖,則會在釋放鎖的同時,喚醒被掛起的線程
-
參考
-
堆的劃分
新生代、老年代,新生代里面有 Eden 空間、 From Survivor 空間、To Survivor 空間;
從內存分配角度看,會在新生代 Eden 空間劃分出多個 線程私有的分配緩沖區(Thread Local Allocation Buffer,TLAB),默認占用 1% -
參數調優
打印 GC 日志:
-XX:+PrintGCDetails
,分析 GC 各階段內存情況,調整對應參數(如 servivor 區的比例、大對象進入老年代的閾值、對象年齡閾值、堆空閑比例、垃圾回收期的選擇、CMS之后整理碎片 等)并定位問題 -
對象逃逸 / 逃逸分析
基本行為就是分析對象動態作用域:當一個對象在方法里面被定義后,可能被外部方法引用
- 方法逃逸:對象作為調用參數傳遞到其他方法中
- 線程逃逸:對象可在其他線程中訪問
針對不會逃逸的對象進行的運行期優化:
-
棧上分配
如果確定一個對象不會逃逸,那么可以直接在棧上分配內存,對象占用的空間可歲棧幀出棧而銷毀;一般應用中,不會逃逸的局部對象所占比例很大,如果能在站上分配,大量的對象會隨著方法的結束自動銷毀,減小了垃圾回收的壓力;
缺點:不能保證逃逸分析的性能收益必定高于其消耗,可能出現效果不穩定的情況
引出另一個問題:對象一定放在堆里面么?
同步消除,沒有逃逸即其他線程無法訪問,即不存在同步問題
-
標量替換
標量(Scalar)是指無法再分解的數據(int, long 等);相反如果可以分解則稱為聚合量(Aggregate),如對象;
將對象使用到的成員變量恢復到原始類型來訪問就叫做標量替換;
如果逃逸分析證明一個對象不會被外部訪問切可別拆散的話,那么程序執行時可能不會創建這個對象,而直接創建若干個使用到的成員變量來代替(直接在棧上分配和讀寫)
參考:
- Java 中的逃逸分析和 TLAB 以及 Java 對象分配
- 深入理解 Java 虛擬機 11.3.5 逃逸分析
- wiki