kyle shanks
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: 'Java GC、內存分配' disqus: kyleAlien --- Java GC、內存分配 === ## OverView of Content 如有引用參考請詳註出處,感謝 :smile: > GC 也就是 [**Garbage Collection**](https://zh.wikipedia.org/wiki/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8)) 它出現的時間比 Java 還早,是一種**自動管理記憶體機制** 至於有 GC、內存動態分配後謂何還需要特別了解? > 當我們需要排查、優化內存時,我們就需要對這些自動化技術進行監控、調節 [TOC] ## GC - 概述 Lisp 是第一門開始使用內存動態分配、內存回收的語言;初期主要思考 3 件事情 1. 哪些物件的內存需要回收 2. 何時需要觸發內存回收 3. 如何回收 ## JVM 選項設置 ### JVM 日誌設定 * [Android 設定](https://developer.android.com/studio/intro/studio-config#customize_vm) : `Help` > `Edit Custom VM Options` * Eclipse 設定 : `Run` -> `Run configurations` -> `類名` -> `arguments` -> `VM arguments` > ![](https://i.imgur.com/1lJ4kxb.png) ### JVM 相關指令 * JVM 除了可以設定日誌選項之外,還可以有多個不同選項可以設定給 JVM | 指令 | 功能 | | ---------------------- | ------------------------------------- | | -XX:+PrintGCDetails | 印出 GC 細節 | | -XX:+PrintGCTimeStamps | 印出 GC 時間戳記 | | -Xloggc:d:/gc.log | GC 日誌存放位子,在 D:/ | | -verbose:gc | 開啟 GC 日誌 | | -Xms20m | JVM 堆的最小值 | | -Xmx20m | JVM 堆的最大值 | | -Xmn10M | 堆內新生代的大小 | | -XX:SurvivorRatio=8 | 新生代中 Eden : Survivor 的比例,8: 2 | | -XX:HeapDumpOnOutOfMemoryError | 當棧溢出時輸出堆疊錯誤訊息 | | -XX:MaxTenuringThreshold | **分代年齡控制** | ### JVM 日誌 & 測試 * JVM 日誌測試碼 ```java= public class TestFlagGC { public static void main(String[] args) { TestGC g1 = new TestGC(); TestGC g2 = new TestGC(); g1.instance = g2; g2.instance = g1; // 移除 GC ROOT g1 = null; // 促進 GC g2 = null; // 促進 GC System.gc(); } } class TestGC { Object instance = null; private byte[] data = new byte[2 * 1024 * 1024]; } ``` * Eclipse 日誌 ```groovy= [0.060s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead. [0.091s][info ][gc,heap] Heap region size: 1M [0.097s][info ][gc ] Using G1 [0.097s][info ][gc,heap,coops] Heap address: 0x0000000081c00000, size: 2020 MB, Compressed Oops mode: 32-bit [0.342s][info ][gc,start ] GC(0) Pause Full (System.gc()) [0.342s][info ][gc,phases,start] GC(0) Phase 1: Mark live objects [0.366s][info ][gc,stringtable ] GC(0) Cleaned string and symbol table, strings: 3081 processed, 15 removed, symbols: 20031 processed, 0 removed [0.366s][info ][gc,phases ] GC(0) Phase 1: Mark live objects 23.512ms [0.366s][info ][gc,phases,start] GC(0) Phase 2: Compute new object addresses [0.366s][info ][gc,phases ] GC(0) Phase 2: Compute new object addresses 0.500ms [0.366s][info ][gc,phases,start] GC(0) Phase 3: Adjust pointers [0.367s][info ][gc,phases ] GC(0) Phase 3: Adjust pointers 0.685ms [0.367s][info ][gc,phases,start] GC(0) Phase 4: Move objects [0.368s][info ][gc,phases ] GC(0) Phase 4: Move objects 0.419ms [0.374s][info ][gc,task ] GC(0) Using 4 workers of 4 to rebuild remembered set [0.376s][info ][gc,heap ] GC(0) Eden regions: 2->0(3) [0.376s][info ][gc,heap ] GC(0) Survivor regions: 0->0(0) [0.376s][info ][gc,heap ] GC(0) Old regions: 0->1 [0.376s][info ][gc,heap ] GC(0) Humongous regions: 6->0 [0.376s][info ][gc,metaspace ] GC(0) Metaspace: 4172K->4172K(1056768K) [0.376s][info ][gc ] GC(0) Pause Full (System.gc()) 7M->0M(8M) 33.401ms [0.376s][info ][gc,cpu ] GC(0) User=0.00s Sys=0.02s Real=0.03s [0.382s][info ][gc,heap,exit ] Heap [0.382s][info ][gc,heap,exit ] garbage-first heap total 8192K, used 973K [0x0000000081c00000, 0x0000000081d00040, 0x0000000100000000) [0.382s][info ][gc,heap,exit ] region size 1024K, 1 young (1024K), 0 survivors (0K) [0.382s][info ][gc,heap,exit ] Metaspace used 4180K, capacity 4486K, committed 4864K, reserved 1056768K [0.382s][info ][gc,heap,exit ] class space used 375K, capacity 386K, committed 512K, reserved 1048576K ``` > [0.342s][info ][gc,start ] GC(0) Pause Full (System.gc()) > > 前面的 0.xxxs 是 GC 發生的時間,**發生暫停的 GC 類型是 System.gc()** **--實做--** > ![](https://i.imgur.com/NqvjSpx.png) ## 物件生死判斷 對於自動回收的關鍵問題其中一個就是,如何判斷物件是否還被需要(物件生死);以下舉幾個常見的方案 ### 方案 - 引用計數 Reference Counting * **引用計數** 原理簡單、判定效率也高,但需要耗費額外空間(小);但是最麻煩的是無法判斷 **相互引用的問題**;當對象被引用時該計數 `+1`,引用失效時計數 `-1`,當引用數計為 0 就任務該物件不會再被使用,GC 可回收 ```java= // 引用計數概念程式 public static void main(String[] args) { A insA = new A(); B insB = new B(); // 互相引用 insA.b = insB; // insB += 1 insB.a = insA; // insA += 1 insA = null; // insB -= 1 insB = null; // insA -= 1 System.gc(); } class A { B b = null; // 內部持有 insB += 1 } class B { A a = null; // 內部持有 insA += 1 } ``` :::danger * **引數計算法不能解決相互引用的問題**: 當對象被相互引用時,就無法確認該物件是否是可以回收的目標 > JVM Hotspot 不採用該方式判斷物件是否可回收 ::: ### 方案 - 可達性分析 Reachability Analysis > 又稱為 **`Tracing GC`** * 選定一些對項作為 GC Roots,並組成根對象集合,當 GC 觸發時搜尋這些根結點,並往下搜尋,如果對象是不可達的代表該對象是可回收的,**分析完後就會給可回收的==對象標記==** > ![](https://i.imgur.com/h9SSQ3Z.png) 作為 GC Roots 根對象主要有 * Java **棧中引用的物件**:也就是方法中所使用到的參數、局部變量、臨時變量 ```java= void functionObject() { String message = new String("Hello"); System.out.println("Message: " + message); } Object obj = new Object(); void showObjectClz() { Class<?> clz = obj.getClass(); System.out.println("Member class: " + clz); } ``` * 方法區中 **靜態屬性引用的物件** ```java= public class MyClz { public static final Object object = new Object(); } class EntryClz { public static void main(String[] args) { System.out.println(MyClz.object.getClass()); } } ``` * 方法區中運行時 **常量池引用** 的物件 ```java= public class MyClz { public static final String TAG = "MyTag"; } class EntryClz { public static void main(String[] args) { System.out.println(MyClz.TAG); } } ``` * 本地方法中 JNI 引用的物件 * 運行中的線程所持有的引用 * 包括同步鎖(`synchronized`)持有的物件 * 由引導類加載器加載的對象(通常就是 JavaSE 的核心類) * GC 控制的對象 :::warning * **物件標記後,是否立刻被回收呢 ?** **不會立刻回收**,首先 GC 並非運行在 MainThread,再者要看物件的引用類型、是否被拯救... 等等狀況 ::: :::success * **完整的 GC Roots** 除了 GC Roots 以外,JVM 還會 **根據用戶所選用的 GC 回收器、當前回收區域、臨時性... 構成完整的 GC Roots** ::: ### Java 引用差異 * JDK 1.2 後就有出現不同的引用方式,**強引用、軟引用、弱引用、虛引用**,以下會使用 `System.gc()` 強制會收並觀察其引用狀況 :::info * 請參考另外一篇 [**Java 引用差別**](https://hackmd.io/Y8kxlR5XSsiQ3uFQMcI_Og?view#Java-%E5%BC%95%E7%94%A8%E5%B7%AE%E7%95%B0) ::: ## GC 目標區塊 * 以 Java 內存分配,運行時的區域分配有兩大特點 1. **共有區**:所有線程共有區塊(**堆、方法區**);一個界面可能有多個實現,一個方法內也有可能創建多個物件 > 由於共享區 堆、棧 的不確定性,所以 **共享區 是主要回收的點**! :::info * 至於是否要全部回收、部份回收,則須看你選用的 GC 回收器的實做是甚麽 > eg. Hotspot 就有分代回收機制 ::: 2. **私有區**:線程私有區塊(**程序計數器、本地方法棧、虛擬機棧**),這個區塊隨著線程生亡,所以每一個棧楨區的分配的內存大小是已知的 > **棧楨大小基本是編譯期間就可知的**,因此這個 **私有區塊不需要特別進行回收**! > ![](https://hackmd.io/_uploads/r1qLJvU3h.png) ### 方法區回收 * 方法區是載入 Class 二進制檔案、常數資料的區塊,該區塊較少被回收(代表仍可能會回收); :::info * JVM 規範中,並沒有強制規定要回收方法區的物件 * 方法區塊的回收相較於堆區回收 CP 值較低 ::: * 如果方法區被回收的話,主要會回收兩個部份的資料 * **無用的常量** * 在程式中沒有再被使用的常量 * **無用的類、界面**(有關到類的卸載) * 目標類 **無任何實例(instance)或是所有的實例都已經被回收**(當然也包括類的子類) * 加載該類的 **ClassLoader 已經被回收**(通常是自訂的 ClassLoader) > ClassLoader 被回收後,就不會有這個類的緩存,GC Roots 就不可達 * 該類在 **堆區中的 `java.lang.Class` 物件不被任何的地方引用** > 反射也無法訪問到 :::warning * 並非所有的 GC 都支援類卸載;像是 **`ZGC` 不支援類卸載** ::: ### 堆區回收 * 堆區是 GC 主力作用的區域,而該區的回收機制又與使用的 GC 類型不同而有不同的管理(可稱為 **GC 收集算法**) > 以下章節會詳細介紹 ## GC 收集算法 這裡主要說明幾種假說、理論還有發展過程 ### 分代收集理論 - 劃分堆區、回收方案 * 目前大多數的 GC 都遵循了 **分代收集(`Generational Collection`)** 的理論、設計;在該理論之上有兩個假說 1. **弱分代假說(`Weak Generational Hypothesis`)**: **朝生夕滅的物件**,像是區域物件(方法內創建的物件) > Hotspot 又將弱分代區塊稱為 **新生代** 2. **強分代假說(`Strong Generational Hypothesis`)**: **熬過多次回收的物件** > Hotspot 又將強分代區塊稱為 **老年代** :::info * 這兩個假說共同奠定了多個 GC 的設置方向;也就是 **將 Java 堆區劃分出多個不同區塊** 來回收 > **主要掃描回收弱分代,關注長期存活的物件,這樣效率更高** ::: * 將堆區劃分出不同區塊後,GC 就可以區域性的回收,藉此產生了 **幾種不同的回收方案**:**^1.^ `Minor GC`、^2.^`Major GC`、^3.^`Mixed GC`、^4.^`Full GC`** > 而回收方案又會影響到回收區域 ``` mermaid graph LR; Minor_GC-->堆區_弱分代; Major_GC-->堆區_強分代; Mixed_GC-->堆區_弱分代; Mixed_GC-->堆區_強分代; Full_GC-->堆區_弱分代; Full_GC-->堆區_強分代; Full_GC-->方法區; ``` 總結 Java 對於物件記憶體區域劃分如下,而區塊何時被回收又如上述 3 種 | 區域 | 細劃分 | Hotspot 稱呼 | 回收方案 | | - | - | - | - | | 堆 | 弱分代 | 新生代 | `Minor GC`、`Major GC`、`Full GC` | | | 強分代 | 老年代 | `Mixed GC`、`Major GC` | | 方法區 | - | 永久代 | `Full GC`| :::info * 而每個劃分區域使用的回收方案又不同(每種虛擬機實現的回收方案都不同),**沒有規定哪個區塊只能用指定算法** > 之後再介紹回收算法,**這些算法都基於 ++分代收集理論++** ::: 3. **跨代引用假說(`Intergenerational Reference Hypothesis`)**: 跨代引用(eg. 弱分代使用強分代的引用)較少,而同代引用較多;也就是說 **我們不該為了少量的跨代引用去掃描整個強分代(老年代)** 因此我們在弱分代之上建立一個全局的數據結構(用來紀錄),它將強分代劃分回多個小區,標示強分代的哪個區塊會存在跨代引用 ```mermaid graph TD subgraph 弱分代 引用 區域跨代_1 end subgraph 強分代 區域_1 區域_2 區域_3 end 區域跨代_1 --> 區域_1 ``` 此後當發生 `Minor GC` 時,就只有包含掛待引用的小塊內存里的物件才會被加入到 GC Roots 掃描(也就不會整強分代都掃描) > 這樣就不會整個強分代都掃描到 ```mermaid graph TD subgraph 弱分代 引用 區域跨代_1 end subgraph 強分代 區域_1 區域_2 區域_3 end 區域跨代_1 --> 區域_1 GC_Roots --> 引用; GC_Roots --> 區域跨代_1; ``` ### 分代收集理論 - 標記清除算法 (Mark-Sweep) * 常用於**堆區**(也就是強分代),遍歷所有區塊,再清除所有的要回收的對象;這會導致兩個問題 1. **內存區塊不完整**,零碎不堪 2. 由於零碎的記憶體區塊,**導致創建物件效率太低**(創建物件要連續的記憶體區塊) > ![](https://i.imgur.com/S6wRvQf.png) ### 分代收集理論 - 標記壓縮算法 (Mark-Compact) * 常用於**堆區**(也就是強分代),標記清除算法的進階,在清除後重新排列對象的記憶體位子,**大多使用在 ==強分代==**;其特點如下 1. 對象的複製移動 2. **引用更新**(相對來講是個重量級操作) :::info * **Stop the World**! 在 GC 發生時 (演算法發生時),只有 GC 線程在運行,其他線程全部暫停(包括用戶線程),**等待 GC 線程執行完引用更新後才能再次運行** > **針對不同的 GC 回收器,Stop the World 會發生在不同的時機** **大量的暫停程序勢必會影響到用戶應用的吞吐量** > ![](https://hackmd.io/_uploads/BkkZXhWR3.png) ::: 3. 沒有內存碎片 (但較耗時) > ![](https://i.imgur.com/d7C4Dfu.png) ### 分代收集理論 - 複製算法(Copying) * 常用於**堆區 - 新生代(也就是弱分代)** 把記憶體切成兩半,當 GC 被觸發時會遍歷一半區塊,把存活的對象複製到另一半(**大多使用在生命週期不長的 ==弱分代==**);並有以下特點 * **優點** 1. 實現簡單,速度快 2. 內存複製,沒有內存碎片 * **缺點** 1. 如果存活對象過多時就會導致大量複製,效率太低 2. 對於空間的利用率只有一半(只剩下一半的空間可以儲存物件) > ![](https://i.imgur.com/Sh8WpP4.png) * 由於上述的空間浪費,所以有另外針對複製算法規劃出不同的分區(不使用 1:1 的空間分配),而是 **採用別種分配方案** 以下 **以 HotSpot 的 Serial、ParNew 為例**,它把新生代(弱分代)再區分為如下空間: 1. **`Eden` 區**:比例為 8;所有的新物件都放置這個區塊 ```mermaid graph TD subgraph 弱分代 Eden_8 end Eden_8 --> 物件_A Eden_8 --> 物件_B Eden_8 --> 物件_C ``` 2. **`Survior` 區**:比例為 2,其中 **又分為 `From Survior`、`To Survior`**; * 每次物件回收後其除 `Eden` 區 ```mermaid graph TD subgraph 弱分代 Eden_8 Survior_2 end ``` * 遺留的物件放到 `To Survior` 區塊 ```mermaid graph TD subgraph 弱分代 Eden_8 Survior_2 end subgraph Survior From_Survior To_Survior end To_Survior --> 物件_A To_Survior --> 物件_B Survior_2 --> From_Survior Survior_2 --> To_Survior ``` :::danger * 存活物件超出空間、或活很久? **再待下一次回收時,如果存留下來的物件超過 10% 的空間(`To Survior` 區塊),則直接移動到老年代(強分代區塊)** 1. 存活對象超過分代年齡,也就是 **多次 Minor Collection**,移動到老年代 (`-XX:MaxTenuringThreshold`) 2. `To Survivor` **空間到達上限**,將存活對象移動到老年代 > ![](https://i.imgur.com/giBTrmD.png) ::: ## GC 回收器 針對不同的堆區會有不同的收集器,而其特色又不一樣,以下主要分為新生代老年代,這樣的分法是依照 JVM 舊的劃分方式,**新的 G1 就++沒有在堆中區分++** 其配對關係如下圖,並 **不是隨意可以配對使用** (有連線才可配合使用) > ![](https://i.imgur.com/J2pr3bl.png) ### 弱分代回收器 > 也就是 Hotspot 的新生代回收器 | 回收器名稱 | 算法 | 類型 | | -------- | -------- | - | | Serial | 複製算法 | 單線程 | | ParNew | 複製算法 | 並行的多線程收集 (利用 CPU) | | Parallel Scavenge | 複製算法 | 並行的多線程收集 (利用 CPU) | * 單線程收集(`Serial`) > ![](https://i.imgur.com/WoNhIwB.png) * 多線程收集 (`ParNew`、`Parallel Scavenge`**同一進程,產生多線程**) > ![](https://i.imgur.com/aHnAW9i.png) :::info * **What is "Safe Point"** ? > JVM 可以快速地完成 GC Root 的初始標記,但如果 Thread 正在執行方法 (棧楨上),就需等到它完全出棧後才能清理,而出棧的點就稱為 SafePoint 以下假設 GC 發生時 Thread 正在 Function 內執行 (以下程式是假設的程式) ```java= import java.lang.ref.WeakReference; public class safePoint { public int work() { int x = 1; int y = 2; int z = (x+y)*10; // JVM 發生GC,但這裡並非 saftPoint ! return z; } public static void main(String[] args) { safePoint s = new safePoint(); WeakReference<safePoint> ws = new WeakReference<>(s); s = null; int a = ws.get().work(); // 在執行完方法後才執行 GC,這裡才是 saftPoint !! if(ws.get() == null) { System.out.println("已回收"); } else { System.out.println("未回收"); } System.out.println("value of a is : " + a); } } ``` ::: ### 強分代 - CMS > 也就是 Hotspot 的老年代 | 回收器名稱 | 算法 | 類型 | | -------- | -------- | - | | Serial Old | 標記壓縮 | 單線程 | | Parallel Old | 標記壓縮 | 並行多線程 | | CMS | ==標記清除== | **並行 & 並發收集器** | * 比較特殊的是 **CMS (Concurrent Mark Sweep)** 使用**標記清除 (無壓縮)**,其注重在反應時間,使用並發的特性產生多的 GC 線程,並**經歷 4 個過程** 1. 初始標記 Stop the world 停止其他線程 2. 並發標記 3. 重新標記 Stop the world 停止其他線程 (時間長一些) 4. 並發清除 * CMS 會造成的一些問題我們也要關注到 1. CPU 敏感: 因為多個了許多的 GC 線程 2. **浮動垃圾** : 在第四個階段的並發清理中產生了要 GC 的對象 3. 內存碎片 : CMS 使用的**標記清除**產生的內存碎片 概念圖 > ![reference link](https://i.imgur.com/NXdWpMZ.png) :::success * **浮動垃圾** ? 它的概念是 **在並發過程中所產生的 GC 標記**,所以會產生在 2~3 和 4階段 1. 第 2~3 階段經由 Stop the World 來停止所有線程,把浮動垃圾都標記 2. 第 4 階段產生浮動垃圾就不算在 CMS 清除之內 * 第 4 階段的浮動垃圾 其實可從 概念圖發現 CMS 在老年代還可以串接 Serial Old,它的作用就是在於處理浮動垃圾,**但由於 Serial Old 是單線程效率較低,有的伺服器會選擇重啟 (這也就是為何遊戲商在半夜維修的原因, 其實就是重新啟動)** > ![](https://i.imgur.com/8O0myGU.png) ::: ### G1 回收器 * G1 並沒有把堆分代,而是**透過等分劃分**,自由度更高 1. Eden: 新生代,使用複製算法 2. Survior: 新生代,使用複製算法 3. Old: 老年代,標記壓縮 4. Humong: 判斷當前對象是否超過 Eden 區的一半,如果超過就放入 Humong 區塊 | 回收器名稱 | 算法 | 類型 | | -------- | -------- | - | | G1 | ==標記清除 + 初始化為 0== | **並行 & 並發收集器** | > ![](https://i.imgur.com/TY5bNRn.png) * G1 的回收方法有接過 4 個階段 1. 初始標記 Stop the World 2. 並發標記 3. 最終標記 : 類似於重新標記 Stop the World 4. **篩選回收** : 對於回收時間較高的先不回收 Stop the World > ![](https://i.imgur.com/dsmScEZ.png) :::success * G1 v.s CMS > 在內存大小 6~8M 之內使用 CMS 效率較高,而超出該範圍則是 G1 效率較高 ::: ## 其他 ### 物件在 JVM 中的生命週期 :::success **標記完成後並不代表該對象會立刻回收**,必須要依照對象的生命週期來判斷 ::: * **創建階段 (`Created`)** 1. 為對象分配內存記憶體空間 2. 建構對象 3. 將超類 (父類) 到子類 static 成員初始化 4. 遞歸調用構造方法 Construct (父類 > 子類) * **應用階段 (`In Use`)** > 對象被創建,並分配給變量時,**這時對象就持有一個引用 (強、軟、弱、虛引用)** * **不可見階段 (`InVisible`)** > 代表當程序找不到對象的強引用,或是已超出作用域時,對象可能作為特殊的 GC Roots 持有,Ex: 對象被本地方法棧中 JNI 引用,或是被其他線程引用 * **不可達階段 (`Unreachable`)** > 並無任何強引用,並且 GC roots 以不可達 * **收集階段 (`Collected`)** > GC 已準備好將該對象內存回收,並重新分配空間,**如果這時 Override finalize 方法則會被調用** * **終結階段 (`Finalized`)** > 對象執行完 finalize 方法後,則該對象進入最後階段 * **對象空間重新分配階段** > 回收該對象,重新排列記憶體位置 ### 逃逸分析 - 棧上分配物件 * 一般來說新物件都是分配在堆上,但是如果經過逃逸分析,並 **沒有逃出該方法就不會在堆上分配,會改為在棧上分配**(這就大大減少了 GC 回收耗費的時間) ```java= // -XX:-DoEscapeAnalysis // -XX:+PrintGC // - 除去 // + 加上 public class testEscape { public static void main(String[] args) { System.out.println("Start"); for(int i = 0; i < 7E7; i++) { Escape(); } System.out.println("End"); } public static void Escape() { String str = new String("123456789"); } } ``` **--實做結果--** 1. **開啟逃逸分析** (預設就開啟)`-XX:+DoEscapeAnalysis` > ![](https://i.imgur.com/RIanjbI.png) 2. **關閉逃逸分析** (必須手動關閉)`-XX:-DoEscapeAnalysis` > ![](https://i.imgur.com/20AZUf3.png) ### GC Roots 細節 - 根節點枚舉 * GC Roots 主要在全局性的引用(常量、靜態屬性... 等等),但是如果在大應用上,要全部掃描並分析會耗費許多時間,**並且在收集實惠暫停用戶線程** :::info * **這類似於 `Stop The World`** * 這 **不同於可達性分析**,可達性分析可以與用戶線程併發,但 **根節點枚舉仍需要暫停** 並取得快照才行 ::: * 當用戶線程暫停後,並不需要一個一個的檢查所有上下文、引用位置,**虛擬機應該有辦法 ++直接的取得存放物件的引用++** **在 HotSpot 中,會使用一組 OopMap 的數據結構來達到該目的!** 當類加載完成後,HotSpot 會把物件內的成員類型都計算出來… 在即時編譯時,也會在特定的位置紀錄下棧、暫存器的引用 > 藉此,GC 在回收時就不用從頭掃描所有物件的上下文 ## Appendix & FAQ :::info ::: ###### tags: `JVM`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully