Q1:JVM由哪些部分组成?
A1:
1. 类加载器子系统 (Class Loader Subsystem)
负责加载、链接和初始化类。它将 .class 文件加载到内存中,并生成对应的 java.lang.Class 对象。
- 功能 :
- 加载 (Loading):查找并读取类的二进制数据。
- 链接 (Linking):验证、准备、解析(将符号引用替换为直接引用)。
- 初始化 (Initialization) :执行类构造器
<clinit>()方法,给静态变量赋初值。
- 分类 (双亲委派模型):
- 启动类加载器 (Bootstrap ClassLoader) :加载核心类库(
rt.jar等),由 C++ 实现。 - 平台类加载器 (Platform ClassLoader):Java 9 之前称为扩展类加载器,加载扩展类库。
- 应用类加载器 (Application ClassLoader):加载用户类路径(Classpath)下的类。
- 启动类加载器 (Bootstrap ClassLoader) :加载核心类库(
2. 运行时数据区 (Runtime Data Area)
这是 JVM 的内存模型,是 JVM 规范中最核心的部分。它分为线程私有 和线程共享两大部分:
线程私有(随线程生灭)
- 程序计数器 (Program Counter Register) :
- 记录当前线程所执行的字节码的行号指示器。
- 是唯一一个在 JVM 规范中没有规定任何
OutOfMemoryError情况的区域。
- Java 虚拟机栈 (Java Virtual Machine Stack) :
- 描述 Java 方法执行的内存模型。
- 每个方法执行时会创建一个栈帧 (Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
- 可能抛出
StackOverflowError(栈深度过深)或OutOfMemoryError(栈扩展失败)。
- 本地方法栈 (Native Method Stack) :
- 与虚拟机栈作用相似,但服务于 Native 方法(通常由 C/C++ 编写)。
线程共享(随 JVM 生灭)
- 堆 (Heap) :
- 最大的一块内存区域。
- 存放对象实例 和数组。
- 是垃圾收集器 (Garbage Collector) 管理的主要区域。
- 可能抛出
OutOfMemoryError。
- 方法区 (Method Area) / 元空间 (Metaspace) :
- 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等。
- 注意:在 Java 8 及之后,方法区的实现由"永久代 (PermGen)"改为"元空间 (Metaspace)",直接使用本地内存,不再受 JVM 堆内存限制(但受物理内存限制)。
3. 执行引擎 (Execution Engine)
负责执行字节码,是 JVM 的核心。
- 解释器 (Interpreter) :
- 逐行读取字节码并立即执行。启动速度快,但运行效率相对较低。
- 即时编译器 (JIT Compiler, Just-In-Time) :
- 将热点代码(频繁执行的代码)编译成本地机器码,并缓存起来。
- 下次执行时直接运行机器码,大幅提升运行效率。
- HotSpot 虚拟机中包含 C1(客户端编译器)和 C2(服务器端编译器)。
- 垃圾回收器 (Garbage Collector) :
- 虽然 GC 主要作用于堆内存,但在架构上常被视为执行引擎的一部分或独立模块。
- 负责自动回收不再使用的对象,释放内存。
4. 本地方法接口 (Native Interface)
- 即 JNI (Java Native Interface)。
- 它的作用是融合不同的编程语言为 Java 所用。
- 当 Java 代码需要调用非 Java 语言(如 C、C++)编写的方法时,通过 JNI 进行交互。
Q2:JVM内存区域是什么样的?
A2:
下图来自Java内存区域详解(重点) | JavaGuide

一、线程私有区域(随线程创建/销毁)
1. 程序计数器(Program Counter Register)
- 作用:记录当前线程正在执行的字节码指令地址(行号指示器)。
- 特点 :
- 是唯一 不会发生
OutOfMemoryError的区域。 - 线程切换时,PC 计数器会保存当前执行位置,恢复时继续执行。
- 若执行的是 Native 方法,计数器值为空(Undefined)。
- 是唯一 不会发生
2. Java 虚拟机栈(Java Virtual Machine Stack)
- 作用 :描述 Java 方法执行的内存模型,每个方法调用会创建一个栈帧(Stack Frame)。
- 栈帧包含 :
- 局部变量表(Local Variables)
- 操作数栈(Operand Stack)
- 动态链接(Dynamic Linking)
- 方法返回地址(Return Address)
- 异常 :
StackOverflowError:线程请求栈深度超过虚拟机允许的最大深度(如递归过深)。OutOfMemoryError:栈可动态扩展时,扩展失败。
3. 本地方法栈(Native Method Stack)
- 作用 :为 Native 方法(C/C++ 编写)服务,与虚拟机栈功能类似。
- 特点:具体实现由虚拟机自行决定,HotSpot 将其与 Java 虚拟机栈合二为一。
二、线程共享区域(随 JVM 启动/销毁)
4. 堆(Heap)
- 作用 :最大的内存区域 ,存放对象实例 和数组。
- 特点 :
- 垃圾收集器(GC)管理的主要区域,几乎所有对象都在堆上分配。
- 可细分为 新生代(Young Generation) 和 老年代(Old Generation) (HotSpot 实现):
- 新生代:Eden 区 + Survivor 区(From/To)
- 老年代:存放长期存活对象
- 可通过
-Xms(初始堆大小)和-Xmx(最大堆大小)调整。
- 异常 :
OutOfMemoryError: Java heap space(堆内存不足)。
5. 方法区(Method Area)
- 作用 :存储类元数据 ,包括:
- 类信息(字段、方法、接口等)
- 运行时常量池(Runtime Constant Pool)
- 静态变量
- JIT 编译后的代码缓存
- 重要演变 :
- Java 7 及之前 :HotSpot 使用 永久代(PermGen) 实现方法区,位于 JVM 堆内。
- Java 8 及之后 :永久代被移除,改用 元空间(Metaspace) ,使用本地内存(Native Memory) ,不再受
-Xmx限制。- 元空间大小默认仅受系统物理内存限制,可通过
-XX:MaxMetaspaceSize限制。 - 类元数据的垃圾回收更高效,避免了永久代常见的
OutOfMemoryError: PermGen space问题。
- 元空间大小默认仅受系统物理内存限制,可通过
- 异常 :
OutOfMemoryError: Metaspace(元空间耗尽)。
💡 运行时常量池 是方法区的一部分,用于存放编译期生成的字面量和符号引用,在类加载后存入。例如
String s = "hello"中的"hello"会进入常量池。
Q3:Java堆空间的基本结构是什么样子的?
A3:

新生代(Young Generation)
- Eden 区 :新对象优先分配于此(TLAB 优化下线程本地分配)。
- Survivor 区 (S0/S1):
- Minor GC 时,Eden + From Survivor 中存活对象复制到 To Survivor。
- 每次 GC 后 S0/S1 角色互换("复制算法"核心)。
- 对象年龄 +1,达到阈值(
-XX:MaxTenuringThreshold,默认15)晋升老年代。
- 特点:高频 Minor GC(Stop-The-World 时间短),回收"朝生夕死"对象。
老年代(Old/Tenured Generation)
- 存放:
- 经多次 Minor GC 仍存活的对象
- 大对象(
-XX:PretenureSizeThreshold直接分配至此) - Survivor 空间不足时的担保晋升对象
- GC 行为:触发 Major GC / Full GC(耗时长,需避免频繁发生)。
Q4:Java虚拟机栈和栈帧是什么?
A3:

