深入理解 JVM 内存模型
详细讲解 Java 虚拟机的内存结构、垃圾回收机制和性能优化
DevClub
15 分钟阅读
JavaJVM性能优化面试
概述
JVM(Java Virtual Machine)内存模型是Java面试中的高频考点,也是理解Java应用性能优化的基础。本文将深入讲解JVM的内存结构和垃圾回收机制。
JVM 内存结构
JVM内存主要分为以下几个区域:
1. 程序计数器(Program Counter)
程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
特点:
- 线程私有
- 唯一不会发生 OutOfMemoryError 的区域
2. Java 虚拟机栈(JVM Stack)
虚拟机栈描述的是Java方法执行的内存模型:每个方法执行时都会创建一个栈帧。
public class StackExample {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = add(a, b);
System.out.println(c);
}
public static int add(int x, int y) {
int result = x + y;
return result;
}
}栈帧包含:
- 局部变量表
- 操作数栈
- 动态链接
- 方法出口
3. 本地方法栈(Native Method Stack)
与虚拟机栈类似,但为Native方法服务。
4. 堆(Heap)
堆是JVM管理的最大一块内存区域,所有对象实例和数组都在这里分配。
特点:
- 线程共享
- 垃圾回收的主要区域
- 可能出现 OutOfMemoryError
public class HeapExample {
public static void main(String[] args) {
// 对象分配在堆上
Person person = new Person("张三", 25);
// 数组也分配在堆上
int[] numbers = new int[1000];
}
}
class Person {
String name; // 引用在栈上,对象在堆上
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}5. 方法区(Method Area)
存储已被虚拟机加载的类信息、常量、静态变量等。
JDK 8 之前: 永久代(PermGen) JDK 8 及以后: 元空间(Metaspace),使用本地内存
垃圾回收(Garbage Collection)
如何判断对象可以回收?
1. 引用计数法
给对象添加一个引用计数器,但无法解决循环引用问题。
2. 可达性分析算法
从GC Roots开始向下搜索,不可达的对象即可回收。
GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
常见的垃圾回收算法
1. 标记-清除(Mark-Sweep)
优点: 实现简单 缺点: 产生内存碎片
2. 标记-复制(Mark-Copy)
优点: 没有内存碎片 缺点: 可用内存减半
3. 标记-整理(Mark-Compact)
优点: 没有内存碎片,不浪费空间 缺点: 移动对象成本高
常见 JVM 参数
# 设置堆大小
-Xms2g # 初始堆大小
-Xmx4g # 最大堆大小
# 设置新生代大小
-Xmn1g # 新生代大小
# 垃圾回收器
-XX:+UseG1GC # 使用 G1 垃圾回收器
# 打印 GC 日志
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:gc.log
# 发生 OOM 时 dump 堆
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heapdump.hprof面试常见问题
Q1: 什么情况下会发生 OutOfMemoryError?
答:
- Java heap space: 堆内存不足
- GC overhead limit exceeded: GC花费时间过多
- Metaspace: 方法区/元空间不足
- Unable to create new native thread: 无法创建新线程
Q2: 如何排查内存泄漏?
答:
- 使用
jmap -heap pid查看堆使用情况 - 使用
jmap -dump导出堆转储文件 - 使用 MAT(Memory Analyzer Tool)分析堆转储
- 查找占用内存最多的对象
- 分析对象的引用链
Q3: 新生代和老年代的区别?
答:
- 新生代(Young Generation): 存放新创建的对象,分为Eden区和两个Survivor区,采用复制算法
- 老年代(Old Generation): 存放长期存活的对象,采用标记-清除或标记-整理算法
对象晋升到老年代的条件:
- 经历多次Minor GC仍存活(默认15次)
- Survivor区放不下
- 大对象直接进入老年代
总结
理解JVM内存模型和垃圾回收机制对于:
- 编写高性能Java应用至关重要
- 排查线上内存问题必不可少
- 面试中经常被考察
建议多动手实践,使用jstat、jmap等工具观察JVM运行状态。