目录
[一:JVM 内存全景图 (Runtime Data Areas)](#一:JVM 内存全景图 (Runtime Data Areas))
[1.1 线程私有区(生命周期与线程同步)](#1.1 线程私有区(生命周期与线程同步))
[1.2 线程共享区(内存分配主体)](#1.2 线程共享区(内存分配主体))
二:类加载系统 (Class Loader Subsystem)
[2.1 加载的五个阶段](#2.1 加载的五个阶段)
[2.2 双亲委派模型](#2.2 双亲委派模型)
三:垃圾回收深度解析 (Garbage Collection)
[3.1 对象存活判定](#3.1 对象存活判定)
[3.2 垃圾回收算法](#3.2 垃圾回收算法)
[3.3 经典垃圾收集器](#3.3 经典垃圾收集器)
[四:JVM 参数与性能调优](#四:JVM 参数与性能调优)
[4.1 常用诊断命令](#4.1 常用诊断命令)
[4.2 示例代码:通过代码感受 JVM 限制](#4.2 示例代码:通过代码感受 JVM 限制)
[五:执行引擎与 JIT 编译](#五:执行引擎与 JIT 编译)
一:JVM 内存全景图 (Runtime Data Areas)
JVM 内存不只是"堆和栈",它是一个精密设计的协作系统。
1.1 线程私有区(生命周期与线程同步)
-
程序计数器 (PC Register):
-
原理:当前线程执行的字节码行号指示器。
-
特点 :占用内存极小,是 JVM 规范中唯一没有规定
OutOfMemoryError的区域。
-
-
虚拟机栈 (JVM Stack):
-
组成 :由一个个栈帧 (Stack Frame) 组成。每个方法从调用到执行完成,对应一个栈帧在栈中入栈到出栈的过程。
-
栈帧内容:局部变量表(存基本类型和引用指针)、操作数栈(计算中间结果)、动态链接、方法出口。
-
异常 :
StackOverflowError(递归太深)或OutOfMemoryError(线程太多)。
-
-
本地方法栈 (Native Method Stack):
- 为虚拟机使用到的
Native方法(如 C/C++ 编写的底层库)服务。
- 为虚拟机使用到的
1.2 线程共享区(内存分配主体)
-
堆 (Heap):
-
分代结构 :分为新生代 (Young Gen) 和 老年代 (Old Gen)。
-
新生代细分:Eden 区、Survivor 0 区、Survivor 1 区(比例通常为 8:1:1)。
-
功能:几乎所有的对象和数组都在此分配空间。
-
-
元空间 (Metaspace):
-
位置 :Java 8 之后,从 JVM 内部移到了本地内存。
-
内容:类信息、常量池、静态变量、JIT 编译后的代码。
-
二:类加载系统 (Class Loader Subsystem)
类加载负责将 .class 文件转换成内存中的 Class 对象。
2.1 加载的五个阶段
-
加载:读取二进制流。
-
验证:格式、元数据、字节码、符号引用验证。
-
准备 :为 static 变量分配内存并初始化为零值 (注意:此时不执行逻辑,
static int a = 123此时 a 是 0)。 -
解析:将常量池内的符号引用替换为直接引用。
-
初始化 :执行
<clinit>()方法(合并所有静态代码块和静态变量赋值语句)。
2.2 双亲委派模型
-
自底向上检查:先看自己加载过没,没加载过问父类,一直问到启动类加载器。
-
自顶向下加载:父类无法加载时,子类才尝试加载。
-
核心逻辑示例:
java// 伪代码演示加载逻辑 protected Class<?> loadClass(String name) { // 1. 先查缓存 Class<?> c = findLoadedClass(name); if (c == null) { // 2. 委派给父类 if (parent != null) c = parent.loadClass(name); else c = findBootstrapClassOrNull(name); } // 3. 实在不行再自己动手 if (c == null) c = findClass(name); return c; }
三:垃圾回收深度解析 (Garbage Collection)
3.1 对象存活判定
-
引用计数法:容易导致循环引用(Java 不采用)。
-
可达性分析 :从 GC Roots(如栈中引用、静态变量、常量池等)出发,无法到达的对象即为垃圾。
3.2 垃圾回收算法
-
标记-复制:新生代使用。内存利用率 90%,不会有内存碎片。
-
标记-清除:老年代使用。会产生碎片。
-
标记-整理:老年代使用。移动存活对象,解决碎片问题,但效率稍低。
3.3 经典垃圾收集器
-
CMS (Concurrent Mark Sweep):追求最短停顿,老年代收集器,并发执行。
-
G1 (Garbage First):将内存切成一个个 Region。不仅回收老年代,也回收新生代,是目前 JDK 8+ 的主流选择。
-
ZGC:Java 11+ 引入,停顿时间不随堆大小增加,始终控制在 10ms 以内。
四:JVM 参数与性能调优
4.1 常用诊断命令
-
jps:查看 Java 进程。 -
jstat -gc <pid> 1000:每秒查看一次 GC 统计。 -
jstack <pid>:查看线程快照(解决死锁、CPU 飙升)。 -
jmap -dump:format=b,file=heap.hprof <pid>:导出堆快照。
4.2 示例代码:通过代码感受 JVM 限制
场景:模拟频繁 Full GC 导致的程序卡顿
java
import java.util.LinkedList;
import java.util.List;
/**
* 设置 JVM 参数: -Xms100m -Xmx100m -XX:+PrintGCDetails
*/
public class FullGCDemo {
private static List<Object> cache = new LinkedList<>();
public static void main(String[] args) throws InterruptedException {
System.out.println("程序启动,开始向堆内存注入数据...");
while (true) {
// 模拟业务逻辑不断产生无法回收的对象(内存泄漏)
for (int i = 0; i < 1000; i++) {
cache.add(new byte[1024]); // 每次加 1KB
}
// 适当移除一些,但总体增长,导致老年代不断填满触发 Full GC
if (cache.size() > 50000) {
for (int i = 0; i < 10000; i++) {
cache.remove(0);
}
}
Thread.sleep(10); // 减慢速度,便于观察控制台 GC 日志
}
}
}
五:执行引擎与 JIT 编译
JVM 并不是单纯的解释执行:
-
解释器:程序启动时立即执行,省去编译时间。
-
JIT 编译器 (Just-In-Time):将经常执行的"热点代码"编译成机器码,提升性能。
-
热点检测:通过计数器(方法调用计数器、回边计数器)识别热点代码。