JVM原理 GC演算法
- JVM的位置
- JVM的體系結構
所謂JVM調優就是調 方法區 和 堆
-
類載入器
作用:載入 Class 檔案
3.1 虛擬機器自帶的載入器
3.2 啟動類(根)載入器
3.3 擴充套件類載入器
3.4 應用程式載入類
- 雙親委派機制
作用:保證安全
java程式往上找,BOOT( rt.jar 優先執行 ) => EXC => APP,找不到才執行自己的方法
-
沙箱安全機制
位元組碼校驗器(bytecode verifier):確保Java類檔案遵循Java語言規範。這樣可以幫助Java程式實現記憶體
保護。但並不是所有的類檔案都會經過位元組碼校驗,比如核心類。JRE = { 類載入器 => 位元組碼校驗器 => 直譯器 }
-
Native
本地方法棧通過JNI連線本地方法介面,本地方法介面呼叫本地方法庫
-
PC暫存器
程式計數器:Program Counter Register
每個執行緒都有一個程式計數器,是執行緒私有的,就是一個指標,指向方法區中的方法位元組碼(用來儲存指向像一條指令的地址, 也即將要執行的指令程式碼),在執行引擎讀取下一條指令,是一個非常小的記憶體空間,幾乎可以忽略不計 -
方法區
方法區是被所有執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構函式,介面程式碼也在此定義,簡單說,所有定義的方法的資訊都儲存在該區域,此區域屬於共享區間
靜態變數 static、常量 final、類資訊 Class(構造方法、介面定義)、執行時的 常量池 存在方法區中,但是例項變數存在堆記憶體中,和方法區無關 -
棧
main 先執行,最後結束
棧:棧記憶體,主管程式的執行,生命週期和執行緒同步
執行緒結束,棧記憶體也就是釋放,對於棧來說,不存在垃圾回收問題
棧:8大基本型別 + 物件引用 + 例項的方法
棧執行原理:棧幀
棧滿了:StackOverFlowError 是錯誤,不是異常
棧 + 堆 + 方法區:互動關係
-
三種JVM
- Sun公司 HotSpot
- Bea公司 JRockit
- IBM公司 J9
-
堆 Heap
一個JVM只有一個堆記憶體,堆記憶體的大小是可以調節的
類載入器讀取了類檔案後,把什麼放到堆中?類,方法,常量,變數 ~ 儲存所有引用型別的真實變數
三個區域:
- 新生區(伊甸園區 Eden,倖存區0區,倖存區1區)
- 養老區
- 永久區
GC垃圾回收,主要是在伊甸園區和養老區
假設記憶體滿了,OOM,堆記憶體不夠
在JDK8以後,永久儲存區改了名字(元空間)
-
新生區
- 類:誕生和成長的地方,甚至死亡
- 伊甸園區:所有的物件都是在伊甸園區new出來的
- 倖存者區(0, 1):
-
老年區
經過研究,99%的物件都是臨時物件
-
永久區
這個區域常駐記憶體的。用來存放JDK自身攜帶的Class物件。Interface元資料,儲存的是ava執行時的一些環境或類資訊,這個區域不存在垃圾回收!關閉虛擬機器,就會釋放這個區域的記憶體
一個啟動類載入了大量的第三方jar包,tomcat部署了太多的應用,大量動態生成的反射類。一旦不斷地被載入,知道記憶體滿,就會出現OOM
- jdk 1.6 之前:永久代,常量池在方法區中
- jdk 1.7:永久代,但是慢慢地退化了,去永久代,常量池在堆中
- jdk 1.8 之後:無永久代,常量池在元空間
-
堆記憶體調優
手動調節堆記憶體
```
OOM之後怎麼解決
1.擴大堆記憶體空間,看結果
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
2.還不行說明程式碼有問題,導致記憶體出問題
```
倖存0區1區,即from區to區
Heap記憶體 = Young + Old,Metaspace元空間,不在堆裡
JVM總記憶體 = Heap + Metaspace,Metaspace增大後會佔用更多空間,導致Heap記憶體減少,Metaspace大到把Heap空間擠沒時,報錯OOM
Young滿了執行GC,一部分消除,一部分進入Old,Old也滿了執行FullGC,一部分消除,一部分進入Metaspace
在一個專案中,突然出現OOM故障,如何排除 ~ 研究為什麼出錯
+ 能夠看到程式碼第幾行出錯:記憶體快照分析工具,MAT(Eclipse),Jprofiler
+ Dubug,一行行分析程式碼
MAT,Jprofiler 作用:
+ 分析Dump記憶體檔案,快速定位記憶體洩漏
+ 獲得堆中的資料
+ 獲得大的物件
+ . . .
IDEA安裝外掛
官網下載
9.2版本的註冊碼
如果堆溢位了,丟出檔案,可以用JProfiler開啟它,檢視記憶體資訊,找出問題
一般式
```java
-Xms //設定初始化記憶體分配大小,預設1/64
-Xmx //設定最大記憶體分配大小,預設1/4
-XX:+PrintGCDetails //列印GC清理資訊
-XX:+HeapDumpOnOutOfMemoryError //OOM dump,別的錯誤同理
```
比如這個記憶體佔了89%,這個 ArrayList 肯定有問題啊
-
GC 垃圾回收演算法
GC的作用區域:堆 和 方法區(屬於堆)
- 新生代
- 倖存區(from,to)
- 老年區
GC兩種型別:輕GC(普通的GC),重GC(全域性GC)
題目:
- JVM的記憶體模型和分割槽~詳細到每個區放什麼?
- 堆裡面的分割槽有哪些? Eden,from,to,老年區,說說他們的特點?
- GC的演算法有哪些?標記清除演算法,標記整理,複製演算法,引用計數法,怎麼用的?
- 輕GC和重GC分別在什麼時候發生?
引用計數法:太low了,不高效,不常用
**複製演算法**:新生代主要用到的演算法
倖存的物件來到倖存區時,會選擇空的to區,然後把from區的物件複製到to區,from變成空的to區,原來的to區變成from區;這樣可以承上啟下,不會卡死
每次GC之後,Eden是空的,to是空的,GC15次之後還活著,進入下一階段,老年區
+ 好處:沒有記憶體的碎片
+ 壞處:浪費了記憶體空間,一半空間永遠是空的
複製演算法最佳使用場景:物件存活度較低的時候,新生區~
**標記清除演算法**:
+ 優點:不需要額外的空間
+ 缺點:兩次掃描,浪費時間,會產生記憶體碎片
**標記壓縮演算法**:再優化,解決了記憶體碎片
+ 缺點:又多一次掃描,還有移動成本
**標記清除壓縮**:先標記清除幾次,再進行壓縮
-
GC 總結
記憶體效率:複製演算法 > 標記清除演算法 > 標記壓縮演算法(時間複雜度)
記憶體整齊度:複製演算法 = 標記壓縮演算法 > 標記清除演算法
記憶體利用率:標記壓縮演算法 = 標記清除演算法 > 複製演算法
GC ~ 分代收集演算法
-
JMM
java memory model ~ java記憶體模型
作用:快取一致性協議, 用於定義資料讀寫的規則(遵守,找到這個規則)。
JMM定義了執行緒工作記憶體和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(Main Memory)中,每個執行緒都有一個私有的本地記憶體(Local Memory)
解決共享物件可見性這個問題:volilate,立即改主存,讓別人拿到新的資料
JMM:抽象理論資訊
volilate關鍵字:解決一致性的