首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

《深入理解 JVM》學習筆記:內存模型、GC 算法、類加載與調優實戰

     編輯:Java 相關知識

基於《深入理解 Java 虛擬機》(第 3 版) 核心知識體系,整理 JVM 運行時數據區、垃圾收集算法與收集器、類加載機制、對象模型等核心內容,並提煉生產環境調優實戰經驗。

一、JVM 運行時數據區 (JDK 8+ 模型)

區域線程共享性核心功能關鍵參數
堆共享對象實例、數組分配主戰場,GC 管理核心區域-Xms, -Xmx, -Xmn
方法區共享類元數據、常量、靜態變量、JIT 編譯代碼-XX:MetaspaceSize, -XX:MaxMetaspaceSize
虛擬機棧隔離方法執行內存模型:棧幀(局部變量表、操作數棧、動態鏈接、返回地址)-Xss
本地方法棧隔離Native 方法調用服務-XX:MaxDirectMemorySize
程序計數器隔離當前線程執行字節碼行號指示器無配置

關鍵變化:JDK 8 移除永久代,類元數據移至元空間(直接內存);JDK 11+ 字符串常量池、靜態變量已完全在堆中。

二、堆內存分代佈局與對象分配流程

堆
├── 新生代
│   ├── Eden 區 (80%)
│   ├── From Survivor (10%)
│   └── To Survivor (10%)
└── 老年代
  • 默認比例:Eden : S0 : S1 = 8 : 1 : 1 (-XX:SurvivorRatio=8)
  • 分配規則:優先 Eden → 大對象直接進老年代(PretenureSizeThreshold) → 長期存活對象進老年代(MaxTenuringThreshold, 默認 15) → 動態年齡判斷
  • TLAB:線程本地分配緩衝,避免多線程分配競爭,-XX:+UseTLAB 默認開啟

三、對象模型:創建、佈局與訪問定位

3.1 對象創建流程

  1. 類加載檢查:常量池定位符號引用 → 類加載/解析/初始化
  2. 內存分配:指針碰撞(Serial/ParNew) 或 空閒列表(CMS/G1) + TLAB
  3. 初始化零值:除對象頭外字段置零
  4. 設置對象頭:HashCode、GC 年齡、鎖狀態、類元數據指針、數組長度
  5. 執行 <init> 方法:按代碼順序賦值、調用構造器

3.2 對象內存佈局

[對象頭 Header] [實例數據 Instance Data] [對齊填充 Padding]
- Mark Word (8/16 bytes): HashCode、GC分代年齡、鎖標記、線程ID、時間戳
- Klass Pointer: 指向方法區類元數據
- Array Length (數組特有): 數組長度

3.3 對象訪問定位

  • 句柄訪問:reference → 句柄池 → 實例數據/類型數據;優點:GC 移動對象只改句柄池,reference 穩定
  • 直接指針訪問:reference → 對象地址 → 對象頭類型指針 → 類元數據;優點:少一次間接訪問,HotSpot 默認

四、垃圾判定算法

算法原理優點缺點應用
引用計數對象引用計數器 +1/-1,為0可回收實時性高、實現簡單無法解決循環引用Python、COM、瀏覽器 JS 早期
可達性分析GC Roots 為起點,搜索引用鏈,不可達即垃圾解決循環引用、準確需 STW 掃描 RootsJava、Go、V8、.NET CLR

GC Roots:虛擬機棧引用對象、本地方法棧引用對象、方法區類靜態屬性/常量引用對象、同步鎖持有對象、JNI 全局引用等。

五、四種引用類型與生命週期

引用類型強度回收時機典型用途API
強引用最強永不回收(OOM 也不回收)普遍對象引用Object obj = new Object()
軟引用次強內存不足前回收緩存、圖片加載SoftReference
弱引用較弱下次 GC 回收監聽器、ThreadLocalMapWeakReference
虛引用最弱回收時收到通知DirectBuffer 內存釋放、堆外內存管理PhantomReference

六、三大經典 GC 算法

算法核心思想優點缺點適用場景
標記-清除標記垃圾 → 統一清除實現簡單效率低、產生碎片老年代 (CMS 初期)
標記-復制劃分兩塊,存活復制到另一塊無碎片、分配快內存利用率 50%新生代 (Serial/ParNew/Parallel Scavenge)
標記-整理標記 → 存活向一端移動 → 清理邊界外無碎片、利用率高移動對象需更新引用、效率較低老年代 (Serial Old/Parallel Old/G1)

七、主流垃圾收集器全景圖

收集器代算法線程特點適用場景
Serial新/老復制/整理單簡單高效、無線程交互開銷Client 模式、單核、小堆
ParNew新復制多Serial 多線程版,配合 CMSServer 模式、多核、需低停頓
Parallel Scavenge新復制多關注吞吐量、自適應調節後台計算、批處理、無交互
Serial Old老整理單Serial 老年代版Client 模式、CMS 降級備選
Parallel Old老整理多Parallel Scavenge 老年代版吞吐量優先場景
CMS老標記-清除多並發標記/清除、低停頓、碎片交互應用、Web 服務、堆<4GB
G1全區域標記-整理(局部復制)多分區、可預測停頓、面向大堆Server 模式、堆>4GB、JDK 9+ 默認
ZGC全區域染色指針+讀屏障多亞毫秒停頓、TB 級堆超大堆、極低延遲敏感
Shenandoah全區域連接矩陣+寫屏障多亞毫秒停頓、與 ZGC 競品超大堆、OpenJDK 主推

CMS 關鍵流程

  1. 初始標記 (STW) - 標記 GC Roots 直接可達
  2. 並發標記 - 遍歷對象圖,與用戶線程並發
  3. 重新標記 (STW) - 修正並發階段變動,多線程並行
  4. 並發清除 - 清理垃圾,與用戶線程並發

缺點:CPU 敏感、浮動垃圾、碎片(Full GC 才整理)。

G1 核心創新

  • Region 分區:堆劃分為 2048 個大小相同 Region(1-32MB),邏輯上屬於新生代/老年代
  • 收集價值優先:後台維護優先列表,優先回收垃圾密度高的 Region
  • 混合收集:Young GC + 部分老年代 Region 同時回收
  • SATB:Snapshot-At-The-Beginning,並發標記用寫屏障記錄舊引用,保證不漏標

八、GC 觸發條件與調優參數

GC 類型觸發條件關鍵參數
Minor GC / Young GCEden 區分配失敗-XX:NewRatio, -XX:SurvivorRatio, -XX:MaxTenuringThreshold
Full GC老年代空間不足、Metaspace 不足、System.gc()、堆內存整理-XX:+DisableExplicitGC, -XX:MetaspaceSize
Mixed GC (G1)老年代佔用達到閾值(InitiatingHeapOccupancyPercent, 默認 45%)-XX:InitiatingHeapOccupancyPercent, -XX:G1MixedGCLiveThresholdPercent

G1 核心調優參數

-XX:+UseG1GC                    # 啟用 G1
-Xmx32g                         # 最大堆 32G
-XX:MaxGCPauseMillis=200        # 目標最大停頓 200ms (核心調優手柄)
-XX:G1HeapRegionSize=16m        # Region 大小 16MB (2的冪, 1-32MB)
-XX:ParallelGCThreads=8         # STW 工作線程數
-XX:ConcGCThreads=2             # 並發標記線程數
-XX:InitiatingHeapOccupancyPercent=45  # 啟動標記周期閾值

避免設置:-Xmn、-XX:NewRatio 等固定新生代大小參數,會干擾 G1 自適應調節。

G1 Full GC 觸發場景與應對

場景原因應對策略
併發模式失敗標記周期未完成老年代已滿增大堆、提前啟動標記(-XX:InitiatingHeapOccupancyPercent)、增加並發線程
晉升/疏散失敗Survivor/老年代無空間容納存活對象增大 -XX:G1ReservePercent、提前啟動標記、增加並發線程
巨型對象分配失敗找不到連續 Region 存放 >50% Region 的對象避免巨型對象、增大 Region Size、增加堆

九、類加載機制:雙親委派與生命週期

9.1 五大階段

  1. 加載:全限定名獲取字節流 → 方法區存儲結構 → 生成 Class 對象
  2. 驗證:文件格式/元數據/字節碼/符號引用 四重校驗
  3. 準備:靜態變量分配內存、賦零值
  4. 解析:符號引用 → 直接引用(類/接口/字段/方法/方法類型/方法句柄/調用點)
  5. 初始化:執行 <clinit>() (靜態變量賦值+靜態代碼塊),父類優先

9.2 主動引用觸發初始化 (5 種)

  1. new、getstatic、putstatic、invokestatic 字節碼指令
  2. 反射調用 java.lang.reflect
  3. 初始化子類觸發父類初始化
  4. JVM 啟動主類
  5. MethodHandle 解析為靜態字段/方法句柄

9.3 雙親委派模型

啟動類加載器 → 擴展類加載器 → 應用程序類加載器 → 自定義類加載器
      ↑              ↑                    ↑                    ↑
   父類加載器      父類加載器           父類加載器           子類加載器
  • 委派機制:收到請求先委派給父類,父類無法加載時子類嘗試
  • 優勢:核心類庫統一由啟動類加載器加載,防止偽造核心類、保證類唯一性
  • 破壞場景:JNDI/SPI/JDBC 需要父類加載器加載用戶代碼(線程上下文類加載器)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved