看到一個 JVM 為什麼被稱作機器的文章,隨手紀錄。
JVM組成
- 加載器;
- 運行時數據區;
- 方法區
- 堆
- Java棧
- 執行引擎;
過程
- 運行時數據區的方法區,從磁盤加載 Java 類字節碼;
- JVM 創建主線程,執行類文件中的 main 方法;
- main 的參數、局部變量入棧;
- (如果有執行)其他方法,創建其他方法的棧楨;
線程安全
- 每個運行方法的線程都有自己的棧;
- 線程的棧彼此隔離;
- 如果在方法裏創建了壹個對象實例,這個對象實例如果沒有被方法返回或者放入某些外部的對象容器中的話,也就是說這個對象的引用沒有離開這個方法,雖然這個對象被放置在堆中,但是這個對象不會被其他線程訪問到,也是線程安全的。
Servlet類
- Web容器創建後,為每個Web請求的用戶分配用戶線程,此時類可以被定義為不是線程安全的;
- 但是不一定會引發線程安全問題:
- Servlet 類 無狀態,狀態被數據庫管理;
GC
- 可達性分析算法,進行對象垃圾的識別:
- 從線程棧幀中的局部變量/方法靜態變量 開始;
- 將這些內容引用的對象進行標記;
- 被標記的對象是否引用其他對象,繼續標記;
- 標記出所有被使用的對象;
- 沒有被標記的對象即可回收垃圾對象;
- 回收方式:
- 清理
- 壓縮
- 複製
清理
- 將對象佔據的內存空間標記為
空閒
; - 在需要新的資源時給出標記
空閒
的內存空間分配新的對象; - 缺陷:
- 標記
空閒
的內存空間連續性;
- 標記
壓縮
- 將存活對象進行拷貝;
- 拷貝組成連續的空間;
複製
- 將堆空間分為兩部分:
- From區:只在這個區域創建對象;
- To區:當From區用完的時候,將標記過的可用對象複製到該區域;
新生代區域、老年代區域
JVM將空間分為新生代 young
和老年代old
區域
- 創建對象只在
新生代
區域創建; - 當
新生代
區域空間不足時,只對新生代
進行垃圾回收; - 這樣需要處理的內存空間相對較小;
新生代
區分為3個區域:
- Eden區
- From區
- To區
當垃圾回收時:
- 垃圾回收是掃描
Eden區
和From區
; - 將存活的對象複製到
To區
; - 交換
From區
和To區
的名稱引用; - 下一次垃圾回收的時候,繼續執行上⬆️面的複製操作;
- 經歷多次後會被複製到
老年代區域
老年代
- 當
老年代
空間滿時; - 會對
新生代
和老年代
進行一次全量
的垃圾回收FullGC
; - 可以根據引用程序的需要對JVM設置:
--XX:NewRatio
;
垃圾回收器
Serial 串行垃圾回收器
JVM 早期的垃圾回收器,只有一個線程;
Parallel 並行垃圾回收器
- 啟動多線程,在多核CPU,效率較前者高;
- 前者串行垃圾回收執行需要停止用戶線程工作;
- 否則會引起
對象引用標記錯亂
(Stop the world);
CMS併發垃圾回收器
- 垃圾回收線程與用戶線程同時併發運行
- 適合 Web 類型對用戶敏感的場景;
G1垃圾回收器
- 將堆空間分為多個子區域;
- 對子區域個字獨立進行回收;
- 可以用戶線程和回收線程併發;
- 適合很多場景;
小問題
OutOfMemoryError
- 堆空間不足,JVM分配空間不支持程序運行;
- 通過調整
-Xmx
參數增加內存空間; - 也可能程序存在內存泄漏;
StackOverflowError
- 線程堆棧空間不足;
- 可能是層次調用過多,導致棧幀過多;
- 檢查是否存在遞歸錯誤;
- 可以通過
-Xss
參數調整增加棧空間大小;
JIT
支持可以用-server
參數打開JIT的C2編譯器來進行優化;