深入理解 JVM 运行时数据区域
本页系统讲解 JVM 内存结构、堆栈区别、方法区、直接内存等核心知识点。
五大运行时数据区域:
public void method() {
int a = 10; // 栈:局部变量
String s = "hello"; // 栈:引用变量
User user = new User(); // 栈:引用,堆:User对象
// 内存分配:
// 栈:a, s, user(引用)
// 堆:User对象实例
}栈帧(Stack Frame):每个方法执行时在虚拟机栈中创建的数据结构。
public int add(int a, int b) {
int c = a + b;
return c;
}
// 栈帧内容:
// 局部变量表:[this, a, b, c]
// 操作数栈:执行 a + b 的临时数据
// 动态链接:指向 add 方法的符号引用
// 返回地址:调用 add 方法的下一条指令地址堆内存分代模型(JDK 8 之前):
对象分配流程:
// 示例:对象分配
public class AllocationTest {
public static void main(String[] args) {
// 小对象:Eden 区
byte[] small = new byte[1024];
// 大对象:直接进入老年代
byte[] large = new byte[10 * 1024 * 1024];
// 长期存活:经过多次 GC 后进入老年代
Object longLived = new Object();
}
}
// JVM 参数:
// -Xmx20m -Xms20m -Xmn10m
// -XX:PretenureSizeThreshold=5m // 大对象阈值
// -XX:MaxTenuringThreshold=15 // 晋升老年代年龄TLAB(Thread Local Allocation Buffer):线程本地分配缓冲区。
作用:避免多线程并发分配内存时的同步开销。
// JVM 参数
-XX:+UseTLAB // 启用 TLAB(默认开启)
-XX:TLABSize=256k // TLAB 大小
-XX:+PrintTLAB // 打印 TLAB 信息方法区(Method Area)存储内容:
public class User {
private static int count = 0; // 方法区:静态变量
private String name; // 堆:实例变量
public static final String TYPE = "USER"; // 方法区:常量
public void method() {
int local = 10; // 栈:局部变量
}
}
// 方法区存储:
// - User 类的元数据
// - count 静态变量
// - TYPE 常量
// - method() 方法的字节码为什么要用元空间?
// JDK 7 永久代参数
-XX:PermSize=64m
-XX:MaxPermSize=256m
// JDK 8+ 元空间参数
-XX:MetaspaceSize=64m
-XX:MaxMetaspaceSize=256m// 字符串常量池
String s1 = "hello"; // 常量池
String s2 = "hello"; // 复用常量池中的对象
System.out.println(s1 == s2); // true
String s3 = new String("hello"); // 堆中新对象
System.out.println(s1 == s3); // false
// intern() 方法
String s4 = s3.intern(); // 返回常量池中的引用
System.out.println(s1 == s4); // true直接内存(Direct Memory):不在 JVM 堆中,而是直接在操作系统内存中分配。
优点:
缺点:
// 使用直接内存
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// JVM 参数
-XX:MaxDirectMemorySize=512m // 限制直接内存大小# 堆内存
-Xms2g # 初始堆大小
-Xmx4g # 最大堆大小
-Xmn1g # 新生代大小
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1
# 栈内存
-Xss256k # 每个线程的栈大小
# 元空间
-XX:MetaspaceSize=128m # 初始元空间大小
-XX:MaxMetaspaceSize=512m # 最大元空间大小
# 直接内存
-XX:MaxDirectMemorySize=512m
# 推荐配置
-Xms4g -Xmx4g -Xmn2g -Xss256k -XX:MetaspaceSize=256m排查步骤:
# 1. 启用 OOM 时自动生成堆转储
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dump.hprof
# 2. 手动生成堆转储
jmap -dump:format=b,file=heap.hprof
# 3. 查看堆内存使用情况
jmap -heap
# 4. 查看对象统计
jmap -histo | head -20
# 5. 使用 MAT 分析
# 下载 Eclipse MAT,打开 heap.hprof 文件
# 查看 Leak Suspects Report