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。
● 初始標記
● 併發標記
● 最終標記
● 篩選回收