1. 程式人生 > 實用技巧 >JVM原理 GC演算法

JVM原理 GC演算法

  1. JVM的位置

  1. JVM的體系結構

所謂JVM調優就是調 方法區 和 堆

  1. 類載入器

    作用:載入 Class 檔案

3.1 虛擬機器自帶的載入器

3.2 啟動類(根)載入器

3.3 擴充套件類載入器

3.4 應用程式載入類

  1. 雙親委派機制

作用:保證安全

java程式往上找,BOOT( rt.jar 優先執行 ) => EXC => APP,找不到才執行自己的方法

  1. 沙箱安全機制

    位元組碼校驗器(bytecode verifier):確保Java類檔案遵循Java語言規範。這樣可以幫助Java程式實現記憶體
    保護。但並不是所有的類檔案都會經過位元組碼校驗,比如核心類。

    JRE = { 類載入器 => 位元組碼校驗器 => 直譯器 }

  2. Native

本地方法棧通過JNI連線本地方法介面,本地方法介面呼叫本地方法庫

  1. PC暫存器

    程式計數器:Program Counter Register
    每個執行緒都有一個程式計數器,是執行緒私有的,就是一個指標,指向方法區中的方法位元組碼(用來儲存指向像一條指令的地址, 也即將要執行的指令程式碼),在執行引擎讀取下一條指令,是一個非常小的記憶體空間,幾乎可以忽略不計

  2. 方法區

    方法區是被所有執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構函式,介面程式碼也在此定義,簡單說,所有定義的方法的資訊都儲存在該區域,此區域屬於共享區間
    靜態變數 static、常量 final、類資訊 Class(構造方法、介面定義)、執行時的 常量池 存在方法區中,但是例項變數存在堆記憶體中,和方法區無關

  3. main 先執行,最後結束

    棧:棧記憶體,主管程式的執行,生命週期和執行緒同步

    執行緒結束,棧記憶體也就是釋放,對於棧來說,不存在垃圾回收問題

    棧:8大基本型別 + 物件引用 + 例項的方法

    棧執行原理:棧幀

棧滿了:StackOverFlowError 是錯誤,不是異常

棧 + 堆 + 方法區:互動關係

  1. 三種JVM

    • Sun公司 HotSpot
    • Bea公司 JRockit
    • IBM公司 J9
  2. 堆 Heap

    一個JVM只有一個堆記憶體,堆記憶體的大小是可以調節的

    類載入器讀取了類檔案後,把什麼放到堆中?類,方法,常量,變數 ~ 儲存所有引用型別的真實變數

    三個區域:

    • 新生區(伊甸園區 Eden,倖存區0區,倖存區1區)
    • 養老區
    • 永久區

GC垃圾回收,主要是在伊甸園區和養老區

假設記憶體滿了,OOM,堆記憶體不夠

在JDK8以後,永久儲存區改了名字(元空間)
  1. 新生區

    • 類:誕生和成長的地方,甚至死亡
    • 伊甸園區:所有的物件都是在伊甸園區new出來的
    • 倖存者區(0, 1):
  2. 老年區

經過研究,99%的物件都是臨時物件
  1. 永久區

    這個區域常駐記憶體的。用來存放JDK自身攜帶的Class物件。Interface元資料,儲存的是ava執行時的一些環境或類資訊,這個區域不存在垃圾回收!關閉虛擬機器,就會釋放這個區域的記憶體

    一個啟動類載入了大量的第三方jar包,tomcat部署了太多的應用,大量動態生成的反射類。一旦不斷地被載入,知道記憶體滿,就會出現OOM

    • jdk 1.6 之前:永久代,常量池在方法區中
    • jdk 1.7:永久代,但是慢慢地退化了,去永久代,常量池在堆中
    • jdk 1.8 之後:無永久代,常量池在元空間
  2. 堆記憶體調優

手動調節堆記憶體

```
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 肯定有問題啊

  1. 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次之後還活著,進入下一階段,老年區

+ 好處:沒有記憶體的碎片
+ 壞處:浪費了記憶體空間,一半空間永遠是空的

複製演算法最佳使用場景:物件存活度較低的時候,新生區~

**標記清除演算法**:

+ 優點:不需要額外的空間
+ 缺點:兩次掃描,浪費時間,會產生記憶體碎片

**標記壓縮演算法**:再優化,解決了記憶體碎片

+ 缺點:又多一次掃描,還有移動成本

**標記清除壓縮**:先標記清除幾次,再進行壓縮
  1. GC 總結

    記憶體效率:複製演算法 > 標記清除演算法 > 標記壓縮演算法(時間複雜度)

    記憶體整齊度:複製演算法 = 標記壓縮演算法 > 標記清除演算法

    記憶體利用率:標記壓縮演算法 = 標記清除演算法 > 複製演算法

    GC ~ 分代收集演算法

  2. JMM

    java memory model ~ java記憶體模型

    作用:快取一致性協議, 用於定義資料讀寫的規則(遵守,找到這個規則)。

    JMM定義了執行緒工作記憶體和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(Main Memory)中,每個執行緒都有一個私有的本地記憶體(Local Memory)

解決共享物件可見性這個問題:volilate,立即改主存,讓別人拿到新的資料

JMM:抽象理論資訊

volilate關鍵字:解決一致性的