JVM调优与原理深度解析:面试必备核心知识点全覆盖

0 阅读14分钟

JVM调优与原理深度解析:面试必备核心知识点全覆盖

🎯 写在前面:JVM(Java Virtual Machine)是Java程序员必须掌握的核心知识,无论是面试还是工作中排查问题,都离不开对JVM的深入理解。这篇文章将带你从原理到调优,系统掌握JVM的核心知识点!

目录导航

  1. JVM整体架构
  2. 类加载机制
  3. 运行时数据区
  4. 垃圾回收算法
  5. 垃圾收集器
  6. JVM调优实战
  7. 常见面试题

一、JVM整体架构

1.1 架构图一览

┌─────────────────────────────────────────────────────────────────┐
│                        JVM 运行时架构                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────────┐  │
│  │   类加载器    │───▶│   运行时     │───▶│   执行引擎       │  │
│  │  ClassLoader │    │   数据区     │    │  Execution Engine │  │
│  └──────────────┘    │  Runtime     │    └──────────────────┘  │
│                      │  Data Areas  │              │          │
│                      └──────────────┘              ▼          │
│                              │              ┌──────────┐      │
│                              └─────────────▶│ 本地方法 │      │
│                                             │   接口   │      │
│                                             │ Native   │      │
│                                             │ Interface│      │
│                                             └──────────┘      │
└─────────────────────────────────────────────────────────────────┘

1.2 各组件职责

组件职责
类加载器负责加载.class文件,生成Class对象
运行时数据区管理内存,分区存放不同类型数据
执行引擎解释执行字节码,JIT编译优化
本地方法接口调用Native方法,连接本地库

二、类加载机制

2.1 双亲委派模型(必问!)

┌─────────────────────────────────────────────┐
│            类加载器双亲委派模型               │
├─────────────────────────────────────────────┤
│                                             │
│              Bootstrap ClassLoader         │
│              (启动类加载器 - C++实现)        │
│                     ▲                        │
│                     │ 委派                    │
│                     │                        │
│              Extension ClassLoader          │
│              (扩展类加载器)                  │
│                     ▲                        │
│                     │ 委派                    │
│                     │                        │
│              Application ClassLoader        │
│              (应用类加载器 - 默认加载器)     │
│                     ▲                        │
│                     │ 委派                    │
│                     │                        │
│              自定义类加载器 (User Defined)   │
│                                             │
└─────────────────────────────────────────────┘

2.2 双亲委派工作流程

加载类请求 → ApplicationClassLoader 
                ↓ 检查是否已加载?
                ├─ 已加载 → 返回Class对象
                └─ 未加载 → 向上委派给父加载器
                              ↓ 检查是否已加载?
                              ├─ 已加载 → 返回Class对象
                              └─ 未加载 → 继续向上委派
                                            ...
                                            ↓ 最终
                                    BootstrapClassLoader
                                            ↓
                                    无法加载 → 向下尝试
                                            ↓
                                    ExtensionClassLoader
                                            ↓
                                    ApplicationClassLoader
                                            ↓
                                    自己加载 → defineClass

2.3 为什么需要双亲委派?

作用说明
安全机制防止核心API被篡改,如自定义String类不会被加载
避免重复加载父加载器已加载的类,子加载器不会再次加载
类的隔离性不同加载器加载的类是不同的类

2.4 类加载过程

类加载完整过程:加载 → 验证 → 准备 → 解析 → 初始化
阶段详细说明
加载(Loading)读取.class文件,生成字节数组,创建Class对象
验证(Verification)验证字节码格式、语义、安全性
准备(Preparation)为类的静态变量分配内存,初始化默认值
解析(Resolution)将符号引用转换为直接引用
初始化(Initialization)执行静态代码块,给静态变量赋值

三、运行时数据区

3.1 内存分区全景图

┌────────────────────────────────────────────────────────────────────┐
│                         JVM 运行时数据区                            │
├──────────────────────────┬─────────────────────────────────────────┤
│                          │                                         │
│     ┌────────────────┐   │   ┌─────────────────────────────────┐   │
│     │   方法区        │   │   │         堆 Heap                  │   │
│     │   Method Area  │   │   │  ┌───────────┬───────────────┐   │   │
│     │  (元空间 Meta)  │   │   │  │  新生代    │    老年代      │   │   │
│     │                │   │   │  │ ┌────┐┌───┐│               │   │   │
│     │ - 类信息       │   │   │  │ │Eden││S0/S1││   Old Gen    │   │   │
│     │ - 静态变量     │   │   │  │ │    ││   ││               │   │   │
│     │ - 常量池       │   │   │  │ └────┘└───┘│               │   │   │
│     │ - JIT代码缓存  │   │   │  │ Young Gen  │               │   │   │
│     └────────────────┘   │   │  └───────────┴───────────────┘   │   │
│                          │   └─────────────────────────────────┘   │
│  ┌───────────────────┐   │   ┌─────────────────────────────────┐   │
│  │    虚拟机栈       │   │   │         本地方法栈               │   │
│  │   JVM Stack       │   │   │      Native Method Stack         │   │
│  │                   │   │   └─────────────────────────────────┘   │
│  │ - 局部变量表      │   │   ┌─────────────────────────────────┐   │
│  │ - 操作数栈        │   │   │         程序计数器              │   │
│  │ - 动态链接        │   │   │        PC Register              │   │
│  │ - 方法返回地址    │   │   └─────────────────────────────────┘   │
│  └───────────────────┘   │                                         │
│                          │                                         │
└──────────────────────────┴─────────────────────────────────────────┘

3.2 各区域详解

3.2.1 堆(Heap)- 重点!
堆内存结构(默认比例 1:2)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Young Generation (新生代)     Old Generation (老年代)
┌─────────────────┐            ┌─────────────────────────┐
│     Eden        │            │                         │
│     8/10        │            │        2/3               │
│                 │            │                         │
├───────┬─────────┤            │                         │
│  S0   │   S1   │            │                         │
│ 1/101/10  │            │                         │
└───────┴─────────┘            └─────────────────────────┘

  ┌─────────────────────────────────────────────┐
  │                   逃逸分析优化              │
  │  标量替换、栈上分配、同步消除               │
  └─────────────────────────────────────────────┘

内存分配公式:

总堆内存 = 新生代 + 老年代
新生代 = Eden + S0 + S1 = 1 + 1/10 + 1/10 = 1.2
老年代 = 2

配置参数:-Xms256m -Xmx256m -Xmn128m  
         → 新生代128M,老年代128M

配置参数:-XX:NewRatio=2  
         → 新生代占1/3,老年代占2/3
3.2.2 虚拟机栈(JVM Stack)
┌─────────────────────────────────────┐
│         虚拟机栈内存模型             │
├─────────────────────────────────────┤
│                                     │
│   ┌─────────────────────────────┐   │
│   │      栈帧 Stack Frame       │   │
│   │  ┌───────────────────────┐  │   │
│   │  │   局部变量表           │  │   │
│   │  │   Local Variables     │  │   │
│   │  ├───────────────────────┤  │   │
│   │  │   操作数栈             │  │   │
│   │  │   Operand Stack       │  │   │
│   │  ├───────────────────────┤  │   │
│   │  │   动态链接             │  │   │
│   │  │   Dynamic Linking     │  │   │
│   │  ├───────────────────────┤  │   │
│   │  │   方法返回地址         │  │   │
│   │  │   Return Address      │  │   │
│   │  └───────────────────────┘  │   │
│   └─────────────────────────────┘   │
│                                     │
│   线程独享,每个线程一个栈          │
│   栈大小:-Xss1m (默认1MB)          │
│                                     │
└─────────────────────────────────────┘
3.2.3 方法区(MetaSpace)
版本实现参数
JDK 7PermGen(永久代)-XX:PermSize=128m
JDK 8+MetaSpace(元空间)-XX:MetaspaceSize=256m
方法区存储内容:
├── 类的元信息(类名、访问修饰符、字段、方法等)
├── 运行时常量池(字面量、符号引用)
├── 静态变量(JDK 7还在堆,JDK 8移至元空间)
├── JIT编译后的代码缓存
└── 虚拟机已加载的类信息

3.3 常见内存溢出(OOM)

区域原因解决方案
堆溢出对象过多无法回收-Xmx 调大,检查内存泄漏
栈溢出递归过深、线程过多-Xss 调大,减少递归
元空间溢出类过多(动态代理、CGlib)-XX:MetaspaceSize 调大
直接内存溢出NIO使用过多-XX:MaxDirectMemorySize

四、垃圾回收算法

4.1 判断对象可回收

4.1.1 引用计数法(不用!)
原理:对象被引用+1,引用失效-1,为0则回收

    ┌─────────┐       ┌─────────┐
    │对象 A   │──────▶│对象 BA引用B
    │计数=1   │       │计数=2A和C都引用B
    └─────────┘       └─────────┘
          ▲
          │
    ┌─────────┐
    │对象 C   │──────▶│对象 B   │  C引用B
    │计数=0   │       │计数=2   │
    └─────────┘       └─────────┘

问题:循环引用无法回收 ❌
4.1.2 可达性分析(GC Roots)- 重点!
可作为GC Roots的对象:
┌─────────────────────────────────────┐
│ ✓ 虚拟机栈(栈帧中的本地变量表)   │
│ ✓ 方法区中静态属性引用的对象       │
│ ✓ 方法区中常量引用的对象           │
│ ✓ 本地方法栈中JNI引用的对象         │
│ ✓ 虚拟机内部引用(Class对象等)     │
│ ✓ 同步锁(synchronized)持有的对象  │
│ ✓ JMXBean、Callback对象等           │
└─────────────────────────────────────┘
可达性分析示例:
        
        ┌─────────────────────────────────────┐
        │           GC Root                  │
        │              ★                     │
        └──────────────┬──────────────────────┘
                       │
              ┌────────▼────────┐
              │     对象 A       │
              └────────┬────────┘
                       │
        ┌──────────────┼──────────────┐
        │              │              │
   ┌────▼────┐    ┌────▼────┐    ┌────▼────┐
   │ 对象 B  │    │ 对象 C  │    │ 对象 D  │──▶ ...
   └────┬────┘    └─────────┘    └─────────┘
        │              ▲
        │              │
        ▼              │
   ┌─────────┐         │
   │ 对象 E  │─────────┘  ← 可达,通过C引用
   └─────────┘           

   ┌─────────┐    ┌─────────┐  ← 不可达,循环引用
   │ 对象 F  │◀───│ 对象 G  │
   └─────────┘    └─────────┘     等待回收 ✓

4.2 标记-清除算法(Mark-Sweep)

原理:
┌─────────────────────────────────────────────┐
│  标记阶段:遍历所有对象,标记存活对象        │
│  清除阶段:统一回收未标记对象                │
└─────────────────────────────────────────────┘

内存示意图:
┌─────────────────────────────────────────────┐
│  清除前:                                    │
│  [●][●][○][●][○][○][●][○][●][○][●][●]     │
│        ↑     ↑  ↑     ↑        ↑  ↑        │
│       存活  死亡存活死亡死亡存活死亡存活...  │
│                                              │
│  清除后:                                    │
│  [●][●][  ][●][  ][  ][●][  ][●][  ][●][●] │
│                                              │
│           ↑                                  │
│         产生内存碎片                         │
└─────────────────────────────────────────────┘

缺点:❌ 产生内存碎片

4.3 复制算法(Copying)

原理:将内存分成两块,每次只使用一块,回收时复制存活对象到另一块

┌─────────────────────────────────────────────┐
│  内存划分:                                   │
│                                              │
│  ┌─────────────────┬─────────────────┐      │
│  │    From Space   │    To Space     │      │
│  │      50%50%        │      │
│  └─────────────────┴─────────────────┘      │
│                                              │
│  使用中              空闲                    │
└─────────────────────────────────────────────┘

回收过程:
┌─────────────────────────────────────────────┐
│                                              │
│  From Space                    To Space     │
│  ┌─────┬─────┬─────┐       ┌─────────────┐   │
│  │  AB  │     │  ───▶ │    A   B    │   │
│  │存活 │存活 │ 死亡 │       │   复制过来   │   │
│  └─────┴─────┴─────┘       └─────────────┘   │
│                                              │
│  优点:无碎片,空间浪费一半                   │
└─────────────────────────────────────────────┘

4.4 标记-整理算法(Mark-Compact)

原理:标记 → 整理(移动存活对象)→ 清除

┌─────────────────────────────────────────────┐
│  整理前:                                    │
│  [●][○][●][●][○][○][●][○][●][○]            │
│                                              │
│  标记存活对象:                              │
│  [★][○][★][★][○][○][★][○][★][○]            │
│                                              │
│  整理后(向一端移动):                       │
│  [★][★][★][★][★][○][○][○][○][○]            │
│   ←存活→←──────── 空闲 ────────→            │
│                                              │
│  优点:✓ 无碎片,适合老年代                  │
│  缺点:需要移动对象,开销大                 │
└─────────────────────────────────────────────┘

4.5 分代收集算法(重点!)

┌──────────────────────────────────────────────────────┐
│              分代收集策略                            │
├──────────────────────────────────────────────────────┤
│                                                      │
│   ┌──────────────────┐    ┌──────────────────────┐  │
│   │    新生代         │    │      老年代           │  │
│   │  (Young Gen)     │    │   (Old/Tenured Gen)   │  │
│   │                  │    │                       │  │
│   │ ┌────┬────┬────┐ │    │                       │  │
│   │ │Eden│ S0 │ S1 │ │    │                       │  │
│   │ │ 8/101/101/10│ │    │        3/4           │  │
│   │ └────┴────┴────┘ │    │                       │  │
│   │                  │    │                       │  │
│   │  Minor GC        │    │      Major/Full GC     │  │
│   │  频繁/快速       │    │      慢/STOP THE WORLD │  │
│   └────────┬─────────┘    └───────────┬───────────┘  │
│            │                         │              │
│            │  晋升                    │              │
│            ▼  (15次GC后)             │              │
│            └─────────────────────────┘              │
│                                                      │
│   回收算法:                   回收算法:            │
│   ┌────────────────┐           ┌────────────────┐   │
│   │  复制算法       │           │ 标记-整理算法   │   │
│   │  (高效/无碎片)  │           │ (无碎片/慢)     │   │
│   └────────────────┘           └────────────────┘   │
│                                                      │
└──────────────────────────────────────────────────────┘

对象分配流程:
┌─────────────────────────────────────────────────────┐
│                                                     │
│  新对象创建 ──▶ Eden区是否有足够空间?               │
│                    │                                │
│         ┌─────────┴─────────┐                      │
│         │是                 │否                     │
│         ▼                   ▼                       │
│    分配到Eden区        Minor GC                      │
│                              │                      │
│              ┌───────────────┼───────────────┐      │
│              │               │               │      │
│              ▼               ▼               ▼      │
│        S0 或 S1        存活对象        存活对象      │
│        (复制算法)     转移到另一块     晋升老年代  │
│                                                     │
└─────────────────────────────────────────────────────┘

五、垃圾收集器

5.1 收集器家族图谱

┌─────────────────────────────────────────────────────────────────────┐
│                        垃圾收集器家族                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   新生代收集器                    老年代收集器                       │
│   ┌─────────────┐                ┌─────────────┐                    │
│   │  Serial GC  │                │ Serial Old  │                    │
│   │  (单线程)   │                │  (单线程)   │                    │
│   └──────┬──────┘                └──────┬──────┘                    │
│          │                              │                            │
│          │ 组合                        │ 组合                        │
│          ▼                              ▼                            │
│   ┌─────────────┐                ┌─────────────┐                    │
│   │ ParNew GC   │                │ CMS GC      │                    │
│   │ (并行)      │ ────────────▶  │ (并发)      │                    │
│   └──────┬──────┘                └──────┬──────┘                    │
│          │                              │                            │
│          │ 组合                        │ 组合                        │
│          ▼                              │                            │
│   ┌─────────────┐                       │                            │
│   │ Parallel    │                       │                            │
│   │ Scavenge    │                       │                            │
│   │ (吞吐量优先)│                       │                            │
│   └──────┬──────┘                       │                            │
│          │                              ▼                            │
│          │                     ┌─────────────────┐                   │
│          │                     │    G1 GC        │                   │
│          │                     │ (_REGION_分代)   │                   │
│          │                     └────────┬────────┘                   │
│          │                              │                            │
│          │                              │ JDK 11+                    │
│          │                              ▼                            │
│          │                     ┌─────────────────┐                   │
│          │                     │    ZGC          │                   │
│          │                     │ (低延迟 <1ms)   │                   │
│          │                     └────────┬────────┘                   │
│          │                              │                            │
│          │                              │ JDK 15+                    │
│          │                              ▼                            │
│          │                     ┌─────────────────┐                   │
│          │                     │    Shenandoah   │                   │
│          │                     │ (低延迟)        │                   │
│          │                     └─────────────────┘                   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

5.2 各收集器对比

收集器线程算法特点适用场景
Serial单线程复制简单高效Client模式、堆小
ParNew多线程复制Serial多核版Server多核首选
Parallel Scavenge多线程复制吞吐量优先后台批处理
Serial Old单线程标记-整理老年代Client模式
Parallel Old多线程标记-整理吞吐量优先组合Parallel
CMS并发标记-清除低延迟响应优先应用
G1并发标记-整理可预测延迟大堆应用
ZGC并发标记-整理<1ms延迟超大堆TB级
Shenandoah并发标记-整理低延迟RedHat

5.3 CMS收集器(重点!)

CMS工作流程(并发标记清理):
┌─────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  初始标记 ──▶ 并发标记 ──▶ 重新标记 ──▶ 并发清理                     │
│  (STW)      (并发)       (STW)       (并发)                          │
│                                                                      │
│  ━━━━━━━━━━━━ STOP THE WORLD ━━━━━━━━━━━━                            │
│  ━━━━━━━━━━━━━━━━━━━ 并发阶段 ━━━━━━━━━━━━━━━━━━━                    │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

详细阶段:
┌─────────────────────────────────────────────────────────────────────┐
│  阶段1:初始标记 (Initial Mark) - STW                               │
│  ├─ 标记GC Roots直接引用的对象                                      │
│  ├─ 速度:快                                                         │
│  └─ 停顿时间:短                                                      │
│                                                                      │
│  阶段2:并发标记 (Concurrent Mark)                                   │
│  ├─ 从GC Roots向下追踪,标记所有存活对象                             │
│  ├─ 速度:慢,与应用并发运行                                          │
│  └─ 特点:可能漏标(remark解决)                                      │
│                                                                      │
│  阶段3:重新标记 (Remark) - STW                                       │
│  ├─ 修正并发标记期间漏标的对象                                        │
│  ├─ 停顿时间:比初始标记长                                            │
│  └─ 多线程并行                                                        │
│                                                                      │
│  阶段4:并发清理 (Concurrent Sweep)                                   │
│  ├─ 清理未标记的垃圾对象                                              │
│  └─ 特点:与用户线程并发,不STW                                       │
└─────────────────────────────────────────────────────────────────────┘

CMS缺点:
┌─────────────────────────────────────┐
│  ❌ 产生内存碎片                      │
│  ❌ 与用户线程争用CPU资源             │
│  ❌ 无法处理浮动垃圾                  │
│  ❌ Concurrent Mode Failure           │
│     (并发失败,启用Serial Old)       │
└─────────────────────────────────────┘

5.4 G1收集器(重点!JDK 11+默认)

G1(Garbage First)核心思想:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                      │
│   将堆划分为多个大小相等的Region(1MB-32MB)                         │
│                                                                      │
│   ┌─────────┬─────────┬─────────┬─────────┐                        │
│   │  Eden   │ Survivor│  Old    │   Hum   │  ← H Humongous大对象    │
│   │  Region │ Region  │  Region │  Region │                        │
│   └─────────┴─────────┴─────────┴─────────┘                        │
│   ┌─────────┬─────────┬─────────┬─────────┐                        │
│   │  Eden   │ Survivor│  Old    │  Hum    │                        │
│   └─────────┴─────────┴─────────┴─────────┘                        │
│   ┌─────────┬─────────┬─────────┬─────────┐                        │
│   │  Eden   │ Survivor│  OldFree   │                        │
│   └─────────┴─────────┴─────────┴─────────┘                        │
│                                                                      │
│   回收时:优先回收垃圾最多的Region                                    │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

G1工作流程:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  Young GC ──▶ Mix GC ──▶ Old Generation                             │
│                                                                      │
│  Young GC:                                                           │
│  ├─ 回收所有年轻代Region                                              │
│  ├─ Eden + Survivor → 新Survivor + 部分Old                          │
│  └─ STW,但可预测                                                    │
│                                                                      │
│  Mix GC:                                                             │
│  ├─ 回收所有年轻代 + 部分老年代                                      │
│  ├─ 计算各Region回收价值(垃圾量/回收成本)                          │
│  ├─ 优先回收高价值Region                                             │
│  └─ 可通过 -XX:MaxGCPauseMillis 控制停顿时间                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

G1关键参数:
┌─────────────────────────────────────┐
│  -XX:+UseG1GC              启用G1  │
│  -XX:MaxGCPauseMillis=200   最大停顿│
│  -XX:G1HeapRegionSize=1m    Region大小│
│  -XX:InitiatingHeapOccupancyPercent=45│
│                            老年代阈值│
└─────────────────────────────────────┘

六、JVM调优实战

6.1 常见调优场景

场景表现解决方案
Young GC频繁Eden区太小,对象分配频繁-Xmn 调大,-XX:SurvivorRatio 调整
Full GC频繁老年代满了或内存碎片-Xmx/-Xms 调大,使用G1
OOMOutOfMemoryError分析dump文件,定位泄漏
GC时间过长Stop The World过长选择低延迟收集器G1/ZGC
CPU高GC线程占用资源减少GC频率,调优参数

6.2 调优参数汇总

堆内存配置:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  -Xms256m          初始堆大小(等价于 -XX:InitialHeapSize=256m)    │
│  -Xmx256m          最大堆大小(等价于 -XX:MaxHeapSize=256m)        │
│  -Xmn128m          新生代大小(等价于 -XX:NewSize=128m)             │
│  -Xss1m            栈大小(等价于 -XX:ThreadStackSize=1m)          │
│                                                                      │
│  -XX:NewRatio=2       新生代:老年代 = 1:2                           │
│  -XX:SurvivorRatio=8  Eden:Survivor = 8:1                          │
│                                                                      │
│  示例计算:                                                          │
│  -Xmx256m -Xmn128m                                                   │
│  → 新生代128M = 80M(Eden) + 16M(S0) + 16M(S1)                       │
│  → 老年代128M                                                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

元空间配置:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  JDK 7及之前:                                                       │
│  -XX:PermSize=128m    永久代初始大小                                 │
│  -XX:MaxPermSize=256m 永久代最大大小                                 │
│                                                                      │
│  JDK 8+:                                                            │
│  -XX:MetaspaceSize=256m  元空间初始大小                             │
│  -XX:MaxMetaspaceSize=512m 元空间最大大小                           │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

GC日志配置:
┌─────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  -XX:+PrintGC                      打印GC日志                        │
│  -XX:+PrintGCDetails              详细GC日志                        │
│  -XX:+PrintGCDateStamps            打印日期时间戳                    │
│  -XX:+PrintHeapAtGC                GC时打印堆信息                    │
│  -Xloggc:/path/to/gc.log          GC日志文件路径                    │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

6.3 内存泄漏排查流程

┌─────────────────────────────────────────────────────────────────────┐
│                     OOM问题排查步骤                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. 添加JVM参数:                                                    │
│     -XX:+HeapDumpOnOutOfMemoryError                                 │
│     -XX:HeapDumpPath=/path/to/dump.hprof                           │
│                                                                      │
│  2. 生成堆转储文件(.hprof)                                         │
│                                                                      │
│  3. 使用分析工具:                                                   │
│     ├─ MAT (Memory Analyzer Tool) - 最常用                          │
│     ├─ VisualVM - JDK自带                                           │
│     ├─ Arthas - 阿里诊断工具                                         │
│     └─ JProfiler - 商业工具                                          │
│                                                                      │
│  4. 分析步骤:                                                       │
│     ├─ 查看dominator tree(内存占用TOP对象)                        │
│     ├─ 查看泄漏嫌疑(Retained Heap最大)                            │
│     ├─ 追溯引用链(GC Roots → 泄漏对象)                            │
│     └─ 定位问题代码                                                  │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

6.4 调优案例

案例1:MySQL连接池导致的OOM

问题现象:
├─ Heap Dump显示大量com.mysql.cj.jdbc.ConnectionImpl对象
├─ 每个连接占用约200KB,1000个连接 = 200MB
└─ 原因:未配置连接池最大连接数或回收策略

解决方案:
├─ 配置Druid/HikariCP最大连接数
├─ 配置连接超时和空闲回收
└─ -Xmx 调大 + 代码修复

案例2:缓存导致的OOM

问题现象:
├─ 使用HashMap做本地缓存
├─ 无上限put导致内存持续增长
└─ Full GC后内存不降

解决方案:
├─ 使用WeakHashMap或Guava Cache
├─ 配置缓存大小上限
├─ 设置TTL过期时间
└─ 改用Redis分布式缓存

七、常见面试题

7.1 JVM基础

Q1:什么是JVM?JDK、JRE、JVM的关系?

┌─────────────────────────────────────┐
│          JDK > JRE > JVM           │
├─────────────────────────────────────┤
│                                     │
│  JDK (Java Development Kit)         │
│  ├─ 包含JRE                          │
│  ├─ 包含编译器 javac                 │
│  ├─ 包含调试工具                     │
│  └─ 开发人员使用                     │
│       │                             │
│       ▼                             │
│  JRE (Java Runtime Environment)    │
│  ├─ 包含JVM                          │
│  ├─ 包含类库                         │
│  └─ 运行人员使用                     │
│       │                             │
│       ▼                             │
│  JVM (Java Virtual Machine)        │
│  ├─ 字节码解释/执行                  │
│  └─ 内存管理                         │
│                                     │
└─────────────────────────────────────┘

Q2:Java代码执行流程?

源码.java 
    ↓ 编译
字节码.class 
    ↓ 类加载
JVM内存 
    ↓ 解释/JIT编译
机器码 
    ↓ 执行
程序运行

7.2 类加载

Q3:什么是双亲委派模型?为什么要用?

答案要点:
1. 加载类时,先向上委派给父加载器处理
2. 父加载器无法加载时,才由自己加载
3. 目的:
   - 避免类的重复加载
   - 保护Java核心API不被篡改
   - 保证类加载的安全性

Q4:类加载的过程?

加载 → 验证 → 准备 → 解析 → 初始化

各阶段详解:
- 加载:读取字节码,生成Class对象
- 验证:验证字节码安全性
- 准备:分配内存,初始化默认值
- 解析:符号引用→直接引用
- 初始化:执行静态代码,赋初始值

7.3 内存与GC

Q5:JVM内存结构?

┌──────────────────────────────────────────────────────────┐
│                     JVM 运行时数据区                      │
├──────────────────────────┬───────────────────────────────┤
│  线程共享:              │  线程私有:                    │
│  ├─ 堆 (Heap)            │  ├─ 虚拟机栈 (Stack)          │
│  └─ 元数据区 (MetaSpace) │  ├─ 本地方法栈 (Native)       │
│                          │  └─ 程序计数器 (PC)           │
└──────────────────────────┴───────────────────────────────┘

Q6:什么是Minor GC和Full GC?区别?

类型触发条件回收区域停顿时间
Minor GCEden区满新生代短,通常<100ms
Full GC老年代满/显式调用全堆+元空间长,通常>500ms

Q7:GC算法有哪些?优缺点?

算法优点缺点适用
标记-清除简单内存碎片老年代
复制无碎片,高效浪费一半空间新生代
标记-整理无碎片移动对象,慢老年代
分代收集综合最优参数调优复杂通用

Q8:线上Full GC频繁怎么排查?

排查步骤:
1. 添加GC参数,打印详细日志
2. 分析GC日志,确定GC类型和频率
3. dump堆内存,分析对象分布
4. 定位泄漏对象,追溯代码
5. 修复代码 + 调整JVM参数

7.4 调优实战

Q9:如何设置JVM参数?给一个8G内存服务器的配置

通用配置(8G服务器,4G给Java):
┌─────────────────────────────────────────────┐
│                                             │
│  -Xms4g -Xmx4g            堆大小            │
│  -Xmn2g                   新生代            │
│  -XX:MetaspaceSize=256m  元空间            │
│  -XX:+UseG1GC             使用G1收集器      │
│  -XX:MaxGCPauseMillis=200 最大停顿200ms    │
│  -XX:+HeapDumpOnOutOfMemoryError           │
│  -XX:HeapDumpPath=/var/log/java/dump.hprof │
│  -Xloggc:/var/log/java/gc.log               │
│                                             │
│  内存计算:                                  │
│  新生代 2G = 1.6G(Eden) + 0.2G(S0) + 0.2G(S1)│
│  老年代 2G                                                   │
│                                             │
└─────────────────────────────────────────────┘

Q10:什么是内存泄漏和内存溢出?

┌─────────────────────────────────────────────────────────┐
│                                                          │
│  内存泄漏 (Memory Leak)                                 │
│  ├─ 定义:对象无法被GC回收,但已不再使用                  │
│  ├─ 原因:长生命周期对象持有短生命周期对象引用            │
│  ├─ 后果:内存逐渐耗尽,最终OOM                          │
│  └─ 示例:静态集合、监听器、未关闭的资源                │
│                                                          │
│  内存溢出 (Memory Overflow)                              │
│  ├─ 定义:内存不够用,无法分配所需内存                    │
│  ├─ 原因:内存泄漏 / 对象过大 / 内存配置过小            │
│  └─ 表现:OutOfMemoryError                              │
│                                                          │
└─────────────────────────────────────────────────────────┘

总结

┌─────────────────────────────────────────────────────────────────────┐
│                     JVM知识体系总结                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐  │
│  │   类加载        │    │   内存管理      │    │   垃圾回收      │  │
│  │                 │    │                 │    │                 │  │
│  │ • 双亲委派      │    │ • 运行时数据区  │    │ • GC算法        │  │
│  │ • 加载过程      │    │ • 堆/栈/方法区  │    │ • 收集器        │  │
│  │ • 类加载器      │    │ • 内存分配      │    │ • 分代回收      │  │
│  │                 │    │                 │    │                 │  │
│  └────────┬────────┘    └────────┬────────┘    └────────┬────────┘  │
│           │                       │                       │            │
│           └───────────────────────┼───────────────────────┘            │
│                                   │                                    │
│                                   ▼                                    │
│                    ┌─────────────────────────┐                          │
│                    │     JVM调优与排查       │                          │
│                    │                         │                          │
│                    │  • 参数配置              │                          │
│                    │  • OOM排查              │                          │
│                    │  • 性能监控             │                          │
│                    └─────────────────────────┘                          │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

写在最后

🎯 核心记忆点

  • 双亲委派模型 = 安全 + 防重复加载
  • 新生代用复制算法,老年代用标记整理
  • G1是JDK 11+默认,低延迟首选
  • 调优原则:Minor GC频繁调大新生代,Full GC频繁选G1或ZGC

📢 讨论话题:大家在工作中遇到过哪些JVM问题?是如何排查和解决的?评论区见!

👇 往期推荐

👍 点赞 + 关注,我们下期再见!