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

JVM 核心知識體系:內存模型、垃圾回收、調優參數全景圖

     編輯:Java 程式設計入門

前言

JVM 是 Java 程序運行的核心容器,掌握其內存結構、垃圾回收機制、類加載過程、調優參數 是高級 Java 工程師的必修課。本文系統梳理 JVM 核心知識點,附圖解與實戰配置。

一、運行時數據區(JDK 1.8+ 視角)

JVM 運行時數據區架構圖

區域線程私有/共享核心功能OOM 風險
程序計數器 (PC Register)私有當前線程執行字節碼行號指示器無
虛擬機棧 (JVM Stack)私有存放棧幀:局部變量表、操作數棧、動態鏈接、方法出口StackOverflowError (遞歸過深) / OOM (線程過多)
本地方法棧 (Native Method Stack)私有服務 Native 方法調用同 JVM Stack
堆共享對象實例分配區,GC 主戰場java.lang.OutOfMemoryError: Java heap space
方法區 (Metaspace, JDK 8+)共享類元數據、常量、靜態變量、JIT 編譯代碼OutOfMemoryError: Metaspace
直接內存共享NIO DirectByteBuffer 分配,不受堆限制OutOfMemoryError: Direct buffer memory

堆內存細分(分代假說)

堆內存分代結構圖

  • 新生代:Eden + 2 個 Survivor (S0, S1),默認比例 8:1:1
  • 老年代:存放長期存活對象
  • 元空間:原永久區移至本地內存,存類元數據

二、對象創建與內存分配流程

  1. 類加載檢查 → 2. 分配內存(指針碰撞/空閒列表) → 3. 初始化零值 → 4. 設置對象頭 → 5. 執行 <init> 構造方法

TLAB (Thread Local Allocation Buffer):每線程在 Eden 劃分專屬分配區,避免多線程 CAS 競爭,提升分配吞吐。

三、垃圾回收核心算法

算法原理優點缺點適用區域
標記-清除標記存活,清除未標記實現簡單產生內存碎片,效率低老年代 (CMS 初期)
標記-整理標記後向一端移動存活對象無碎片需移動對象,更新引用,停頓長老年代 (Parallel Old, G1 混合)
復制劃分兩塊,存活對象復制到另一塊無碎片,分配快浪費 50% 空間新生代 (Serial, Parallel, G1 Young)
分代收集按對象壽命分代,不同代用不同算法綜合上述優點實現複雜全堆 (所有商用 GC)

可達性分析判斷存活

GC Roots 包括:棧中引用、方法區靜態屬性/常量引用、本地方法棧 JNI 引用、同步鎖持有對象、類對象等。不可達對象標記為可回收。

四、主流垃圾收集器對比

收集器模式適用區域算法特點啟用參數
Serial單線程新生代/老年代復制/標記-整理簡單高效,Client 模式默認-XX:+UseSerialGC
ParNew多線程新生代復制Serial 多線程版,配合 CMS-XX:+UseParNewGC
Parallel Scavenge多線程新生代復制關注吞吐量,自適應調節-XX:+UseParallelGC
Parallel Old多線程老年代標記-整理Parallel Scavenge 老年代版-XX:+UseParallelOldGC
CMS並發老年代標記-清除低停頓,碎片整理需 Full GC-XX:+UseConcMarkSweepGC
G1並發+分區全堆標記-整理+復制大堆、可預測停頓、Region 劃分-XX:+UseG1GC (JDK 9+ 默認)
ZGC並發全堆着色指針+讀屏障亞毫秒停頓,TB 級堆-XX:+UseZGC (JDK 11+)
Shenandoah並發全堆轉發指針+寫屏障低停頓,與 ZGC 類似-XX:+UseShenandoahGC

CMS 關鍵階段

  1. 初始標記 (STW) — 標記 GC Roots 直接關聯
  2. 並發標記 — 遍歷對象圖
  3. 重新標記 (STW) — 修正並發階段變動
  4. 並發清除 — 清理垃圾

缺陷:碎片化、浮動垃圾、CPU 敏感。JDK 9 標記棄用,JDK 14 移除。

G1 核心概念

  • Region:堆劃分為 2048 個等大 Region (1-32MB),每個 Region 可扮演 Eden/Survivor/Old/Humongous
  • Remembered Set (RSet):記錄跨 Region 引用,避免全堆掃描
  • Mixed GC:若老年代佔比超閾值,混合回收部分老年代 Region
  • Full GC 降級:單線程 Serial Old,極慢,需避免

五、JVM 核心調優參數速查表

# 堆基礎
-Xms4g -Xmx4g                 # 初始/最大堆,建議設為相同值避免震蕩
-Xmn1.5g                      # 新生代大小,建議堆的 3/8
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m  # 元空間

# 線程棧
-Xss1m                        # 每線程棧大小,默認 1M,輕量服務可降至 512k/256k

# 分代比例
-XX:NewRatio=2                # 老年代:新生代 = 2:1
-XX:SurvivorRatio=8           # Eden:Survivor = 8:1
-XX:MaxTenuringThreshold=15   # 晉升老年代年齡閾值,默認 15 (CMS/G1 動態)

# GC 選擇 (三選一)
-XX:+UseParallelGC -XX:+UseParallelOldGC   # 吞吐量優先,批處理、計算型
-XX:+UseG1GC                               # 綜合場景,大堆(>6G)、低延遲要求
-XX:+UseZGC                                # 超大堆(>16G)、極致低延遲 (JDK 11+)

# G1 專屬
-XX:MaxGCPauseMillis=200      # 目標停頓時間
-XX:G1HeapRegionSize=16m      # Region 大小 (1-32M, 2 的冪)
-XX:InitiatingHeapOccupancyPercent=45  # 觸發並發標記堆佔比

# GC 日誌 (JDK 8/9+ 統一)
-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=100m

# OOM 保留現場
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps/heap.hprof
-XX:OnOutOfMemoryError="bash /scripts/alert.sh %p"

六、Java 內存模型 (JMM) 與并发核心

可見性

主內存 vs 工作內存。volatile 保證讀寫直接作用主內存,禁止指令重排,實現可見性(不保證原子性)。

有序性

happens-before 規則:程序順序、鎖規則、volatile 規則、線程啟動/終止、傳遞性。synchronized、Lock 保證原子性+可見性+有序性。

七、常見 OOM 場景與排查

異常原因排查工具/手段
Java heap space堆不足/洩漏/對象過大MAT 分析 heap dump、GC 日誌、Arthas dashboard
Metaspace類加載過多/動態代理/反射jcmd PID VM.metaspace、調大 MaxMetaspaceSize
StackOverflowError遞歸過深/循環調用增大 -Xss、優化算法、尾遞歸
Unable to create native thread線程數超限/棧過大ulimit -u、降低 -Xss、線程池限流
Direct buffer memoryNIO 直接內存洩漏-XX:MaxDirectMemorySize、Cleaner 清理、檢查 Netty 泄漏

八、生產級監控與調優工具鏈

  • JConsole / VisualVM / JMC:圖形化監控堆、線程、類、GC
  • Arthas:線上診斷神器,dashboard、trace、watch、heapdump
  • GCeasy / GCEasy.io:GC 日誌在線分析
  • MAT (Memory Analyzer Tool):Heap Dump 深度分析,支配者樹、洩漏疑點報告
  • JFR (Java Flight Recorder) + JMC:JDK 11+ 內建低開銷事件記錄

九、面試高頻題速記

  1. 堆 vs 棧:堆存對象共享,棧存棧幀私有,棟樑關係。
  2. 對象大小:空 Object 16B (8B Header + 8B 填充),引用 4B (壓縮指針)。
  3. Minor GC vs Full GC:新生代 vs 全堆+元空間,觸發條件不同。
  4. 雙親委派模型:Bootstrap → Extension → Application,打破場景 (SPI、熱部署)。
  5. finalize() 已棄用:不可靠、性能差,替代 Cleaner/try-with-resources。
  6. 為什麼不建議手動 System.gc():觸發 Full GC,STW 長,破壞分代假說節奏。

總結

JVM 調優無銀彈,核心在於理解內存模型、選對收集器、設定合理參數、持續觀測指標。建議從 G1 (JDK 11+ 默認) 起步,根據業務延遲/吞吐目標微調 MaxGCPauseMillis、InitiatingHeapOccupancyPercent,配合 Xlog:gc* 日誌與 Arthas 實時診斷,逐步構建穩定高效的運行時環境。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved