前言:
本文整理自《深入理解Java虛擬機》
Java 內(nèi)存區(qū)域
1、Java虛擬機運行時數(shù)據(jù)區(qū)

截屏2019-12-10下午6.33.29.png
- 程序計數(shù)器:線程私有,當前線程執(zhí)行的字節(jié)碼的行號指示器。(如果是Native方法則為空);
- 虛擬機棧:線程私有,與線程的生命周期相同,用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口信息等,每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
- 本地方法棧:與虛擬機棧的作用相似,他們之間的區(qū)別是虛擬機棧是為執(zhí)行Java方法服務,而本地方法棧則為虛擬機用到的Native方法服務。
- Java堆:線程共享,用于存放對象實例和數(shù)組。
- 方法區(qū):線程共享,用于存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。
- 常量池:方法區(qū)的一部分,用于存放編譯器生成的各種字面量和符號引用。
- 直接內(nèi)存:不是虛擬機運行時數(shù)據(jù)區(qū)的一部分。它可以通過Native函數(shù)庫直接分配堆外內(nèi)存,然后通過一個存儲在堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作,這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回復制數(shù)據(jù)。
2、虛擬機對象的創(chuàng)建
- 將檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被夾在、解析和初始化過,如果沒有,那么必須先執(zhí)行相應的類加載過程。
- 虛擬機需要將分配的內(nèi)存空間都初始化為零值(不包括對象頭)。
- 虛擬機要對對象進行必要的設置,例如這個對象是那個類的實例、如何才能找到類的元數(shù)據(jù)信息、對象的哈希碼、對象的GC分代年齡等信息。
以上對于虛擬機來說,一個對象已經(jīng)產(chǎn)生了,但從Java程序的角度來看,對象創(chuàng)建才剛剛開始————<init>方法還沒有執(zhí)行,所有的字段都還為零。
3、對象的內(nèi)存布局
對象在內(nèi)存中存儲的布局可以分為三塊區(qū)域:對象頭(Header)、實例數(shù)據(jù)(Instance Data)和對齊填充(Padding)
- 對象頭:包含兩部分
- 第一部分:用于存儲對象自身的運行時數(shù)據(jù),例如哈希碼、GC分代年齡、鎖標志狀態(tài)、線程持有的鎖、偏向線程的ID、偏向時間戳等。官方稱作為:Mark Word。
- 第二部分:類型指針,即對象指向他的類元數(shù)據(jù)的指針,虛擬機通過這個指針來確定這個對象是那個類的實例。
- 實例數(shù)據(jù):存儲對象的有效信息,也是在程序代碼中所定義的各種類型字段的內(nèi)容。
- 對齊填充:該內(nèi)容并不是必然存在的,也沒有特別的意義,它僅僅起著占位符的作用。
Java垃圾回收器與內(nèi)存分配策略
垃圾回收需要解決的三個問題:
* 哪些內(nèi)存需要回收?
* 什么時候回收?
* 如何回收?
- 回收算法
引用計數(shù)算法:給對象加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器值加1;當應用失敗時,計數(shù)器減1;任何時刻計數(shù)器為0的對象就是不可能被使用的。
問題:引用計數(shù)法很難解決對象間互相循環(huán)引用的問題。-
可達性分析算法:這個算法基本的思路是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。
可達性分析算法中即使不可達的也并不是非死不可的,要真正
GC Roots的對象包含一下幾種:
* 虛擬機棧(棧幀中的本地變量表)中引用的對象。
* 方法區(qū)中類靜態(tài)屬性引用的對象。
* 方法區(qū)中常量引用的對象。
* 本地方法棧中JNI(即一般說的Native方法)引用的對象。
引用分類:JDK1.2后,Java對引用的概念進行了擴充,將引用分為強引用、軟引用、弱引用和虛引用。
- 垃圾收集算法
- 標記-清除算法
- 復制算法
- 筆記-整理算法