快速理解JAVA虛擬機一些要點

JAVA虛擬機是理解JAVA特性、JAVA多線程編程的重要基礎,這篇文章整理自:《深入理解JAVA虛擬機》。 想要通過最簡單的語言讓讀者快速回憶起JAVA虛擬機的一些基礎要點。


1 JAVA內存區域

 


線程共享區域

方法區:存放類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據

運行時常量池:是方法區的一部分

堆:存放對象實例。(因爲逃逸分析,對象不一定在堆上分配)

 

線程私有區域

程序計數器:存放程序計數器       

虛擬機棧:局部變量,操作數棧,動態鏈接等

本地方法棧:與虛擬機棧類似,但是是Native方法

 

PS:

注意和JMM所區別,JMM和上面的內存是不同級別的劃分,是沒有關係的。

JMM關注的是共享內存和工作內存,即線程共享的內存(不包含本地量)。



2 對象創建的過程

1. 遇到一個new指令後,先去常量池定位到一個類的符號引用,檢查這個符號引用代表的類是否已經被加載,解析和初始化過,如果沒有,先執行類加載過程

2. 類加載檢查通過後,爲新生對象分配內存

3. 內存分配完成後,將分配到的內存空間都初始化爲零值(保證實例變量不賦值也能直接使用)

4. 配置對象(比如這個對象是哪個類的實例,如何才能找到類的元數據信息)。

5. 執行程序員的初始化代碼


3. 類加載過程

1. 裝載:導入class文件

2. 連接

a. 驗證:檢查class文件正確性

b. 準備:給類中的靜態變量分配存儲空間並且設置初始零值(但被final修飾的時候直接初始對應值)

c. 解析:將符號引用轉化爲直接引用(可選)

3. 初始化:對靜態變量和靜態代碼塊執行初始化工作(靜態變量的初始化,靜態代碼塊的執行等)

4. 使用

5. 卸載

 

4 判斷對象生存狀態的算法

1. 引用計數算法
給對象添加一個計數器,每當有地方引用它的時候,計數器加一。但容易死鎖

2. 可達性分析:(大部分JAVA虛擬機使用的)
從GC root追蹤對象,若不可達,則判斷爲死亡,常見GC root 包括

a. 虛擬機棧中的對象

b. 方法去中靜態屬性引用的對象

c. 方法去中常量引用的對象

d. 本地方法棧中JNI(native方法)引用的對象

 

5 對象被回收過程

1. 判斷對象不可達,進入下一步

2. 篩選:

a. 沒有finalize()方法或者已經執行過一次finalize()方法直接回收

b. 有finalize方法並且未執行過,進入第3步

3. 將對象置於F-Queue中。讓虛擬機一個低優先級的Finalizer線程去執行它,這裏所謂的“執行”只是虛擬機會觸發這個方法,但不承諾等它結束。之後GC會再次標記這個隊列的對象,如果沒有在finalize裏重新鏈接,則回收。

 

關於finalize方法

JAVA不鼓勵使用finalize方法,因爲它不能保證被執行,不能保證被執行完畢,而且代價高昂。

 

6 GC算法

1. 標記-清除(mark-sweep)
先標記所有要回收的,再清除。
優點:是最基本的回收算法
缺點:1.標記清除過程效率不高 2.產生大量內存碎片

2. 複製(copying)
把內存分兩份,每次只使用一塊,當一塊用完時,複製到另一塊上再清空
優點:1.不必考慮內存碎片 2.只要移動堆頂指針,按順序分配,簡單高效
缺點:1. 浪費內存 2. 對象存活率較高的時候需要過多操作 3.如果不使用50%對分策略,老年代需要空間擔保策略

3. 標記-壓縮(mark-compact)
標記完了之後,向一端compact內存

4. 分代(generation collecting):

a. 新生代: 複製算法

b. 老年代:標記-清理  或者 標記-整理

 

7 新生代中Copying算法



在copying算法中不一定非要對半分, 新生代中98%的對象都是朝生夕死,所以將內存劃分爲一塊Eden和兩塊survivor。

每次只使用Eden空間+1塊survivor,回收的時候把所有存活對象複製到另一塊survivor中。再清空。

這樣有個問題,就是當一個survivor不夠放下所有存活對象的時候,需要依賴其他內存(這裏指老年代)進行內存擔保。


8 Stop-the-world  垃圾收集器

Stop-the world從應用中停下來進入到GC執行過程中去。一旦Stop-the-world發生,除了GC所需要的線程外,其他線程都將停止工作,中斷了的線程直到GC任務結束才繼續它們的任務。GC調優通常是爲了改善Stop-the-world的時間。

 

根據不同的代,有不同的垃圾收集器:

 

 

Serial:

過程:採用複製算法,採用單線程,停下所有工作來進行GC

優點:簡單而高效,沒有多線程的開銷    

缺點:停頓

適用:對運行在client模式下的虛擬機來說是一個很好的選擇(如用戶桌面應用場景中,分配給新生代的內存至多幾百兆)

 

 

ParNew:

過程:採用複製算法,Serial的多線程版本。

優點:併發執行,除了Serial外,只有它能和CMS配合工作

缺點:當CPU數目不多時候,開銷過大

適用:Server模式

 

 

    

 

Parallel Scavenge:

過程:採用複製算法,“吞吐量優先”收集器,

優點:爲高吞吐量設計(少考慮交互延遲,多考慮利用CPU)

缺點:交互延遲

適用:後臺計算而不需要太多的交互任務

 

 

 

 

Serial Old:

過程:採用標記-整理算法,單線程

主要意義:在client模式下使用,或者作爲CMS的後備 


 

Parallel Old:

過程:採用標記-整理算法,多線程

適用:可以跟parallel scavenge配合使用,“吞吐量優先”

 

CMS:

過程:採用標誌-清除算法

● 初始標記:stop-the-world 標記一下GC root能直接關聯到的對象,速度很快

● 併發標記:GC root tracing

● 重新標記:stop-the-world 修正併發標記時出現的變化

● 併發清除:清除

優點:避免了耗時最長的兩個過程的停頓,注重響應速度

缺點:1. 耗費CPU。 2. 無法處理浮動垃圾,老年代空間使用率低。 3. 空間碎片

 

 

 

G1:

過程:新生代和老年代不再物理隔離,並行和併發,分帶收集,整體採用“標記-整理”(部分採用複製),可預測的停頓時間模型(有計劃地避免全區域垃圾收集)。 G1 跟蹤各個Region裏面垃圾堆積的價值大小,在後臺維護一張優先列表,每次根據允許的收集時間,有限回收價值最大的Region。

● 初始標記

● 併發標記

● 最終標記

● 篩選回收

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章