一、整体概览
JVM(Java Virtual Machine)在运行时会把内存划分成若干区域,用于存放不同类型的数据和执行信息。JDK 8 之后的 JVM 内存结构主要分为以下部分:
线程私有区域
程序计数器 (Program Counter Register)
Java 虚拟机栈 (JVM Stack)
本地方法栈 (Native Method Stack)
线程共享区域
堆 (Heap)
方法区 (Method Area,JDK 8 以后由元空间 Metaspace 替代)
直接内存 (Direct Memory)
不属于 JVM 规范中的运行时数据区,但常在 NIO 中用到。
二、各个区域详解
1. 程序计数器(PC 寄存器)
作用:记录当前线程执行的字节码指令的地址。
特点:
每个线程独有,线程切换时能恢复到正确执行位置。
不会出现 OOM。
2. Java 虚拟机栈(JVM Stack)
作用:描述方法执行的内存模型。
栈帧(Stack Frame):每个方法执行时会创建一个栈帧,存放:
局部变量表(Local Variable Table)
操作数栈(Operand Stack)
动态链接(指向运行时常量池的方法引用)
方法返回地址
异常:
栈深度超过限制:
StackOverflowError
内存不足无法扩展:
OutOfMemoryError
3. 本地方法栈(Native Method Stack)
作用:为虚拟机调用 本地方法(Native,通常是 C/C++ 代码) 服务。
异常:也可能抛出
StackOverflowError
和OutOfMemoryError
。
4. 堆(Heap)
作用:存放对象实例和数组,是 GC 管理的主要区域。
特点:
线程共享
JVM 启动时创建
可以物理上不连续,但逻辑上连续
内存溢出:对象过多且无法回收时,抛出
OutOfMemoryError: Java heap space
。分代模型(常见):
新生代(Young Generation):Eden + Survivor(From/To)
老年代(Old Generation)
(JDK 8 前)永久代(PermGen,存放类元数据等,已被移除)
5. 方法区(Method Area) / 元空间(Metaspace)
作用:存放类的结构信息,例如:
类的元数据(类名、修饰符、方法、字段等)
常量池(运行时常量池)
静态变量
JIT 编译后的代码
JDK 8 前:方法区在堆中实现,称为 永久代(PermGen)。
JDK 8 后:使用本地内存(Native Memory)实现,叫 元空间(Metaspace)。
异常:
OutOfMemoryError: Metaspace
。
6. 运行时常量池(Runtime Constant Pool)
作用:存放编译期生成的各种字面量和符号引用(类名、方法名、字段名、字符串字面量等)。
位置:属于方法区的一部分。
特点:可以动态扩展,比如
String.intern()
会往常量池添加字符串。异常:
OutOfMemoryError: PermGen space
(JDK 7 及以前)或OutOfMemoryError: Metaspace
(JDK 8+)。
7. 直接内存(Direct Memory)
作用:堆外内存,主要由 NIO 分配,性能更高(避免 Java 堆与 Native 堆的数据拷贝)。
分配方式:
ByteBuffer.allocateDirect()
。异常:超出限制会抛出
OutOfMemoryError: Direct buffer memory
。
三、总结口诀
线程私有:程序计数器、虚拟机栈、本地方法栈
线程共享:堆、方法区
非规范但常用:直接内存
JVM 垃圾收集算法
一、常见的 GC 算法
1. 标记-清除(Mark-Sweep)
过程:
标记:从 GC Roots 出发,标记所有可达对象。
清除:回收未被标记的对象。
优点:实现简单。
缺点:
内存碎片化(清理后留下很多不连续的内存块)。
分配大对象时可能找不到足够连续内存 → OOM。
2. 复制(Copying)
过程:
将内存划分为两块(From / To)。
每次只使用一块(From)。
GC 时,把存活对象复制到另一块(To),再清空原来的那块。
优点:
没有碎片问题。
分配效率高(指针碰撞)。
缺点:
浪费一半内存空间。
如果存活对象很多,复制开销大。
适用场景:新生代(大多数对象很快死亡,存活对象少,复制成本低)。
3. 标记-整理(Mark-Compact)
过程:
标记所有存活对象。
将存活对象移动到内存一端,保持连续。
清理边界外的内存。
优点:解决了内存碎片问题。
缺点:对象移动,性能开销较大。
适用场景:老年代(存活对象较多,复制算法不适合)。
4. 分代收集(Generational Collection)
思想:根据对象生命周期长短,把堆分为不同区域,用不同的算法:
新生代:对象朝生夕死,采用 复制算法。
老年代:对象存活率高,采用 标记-清除 或 标记-整理。
优点:综合利用不同算法的长处,是目前商用 JVM 的主流。
二、特点对比表
三、扩展(回答加分点)
可达性分析(GC Roots):JVM 判断对象是否存活的主流方法(栈引用、本地方法栈、静态变量、常量池引用等)。
现代垃圾收集器(基于这些算法实现):
Serial、Parallel Scavenge、CMS、G1、ZGC、Shenandoah。
面试时可以顺带提到 CMS(并发标记清除,低停顿) 和 G1(分区化、可预测停顿时间),加分。