运行时数据区域
运行时数据区域
JVM 运行时数据区域分为五个部分:
- 方法区
- 堆
- 虚拟机栈
- 本地方法栈
- 程序计数器
特殊区域:直接内存

一、方法区
主要作用
JVM方法区主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
二、堆
主要作用
VM堆是用于存储对象实例和数组的内存区域。
堆是 Java 虚拟机管理的内存中最大的一块,是被所有线程所共享的一块区域。
所有的对象及数组都是在堆上面分配空间存储,也是被垃圾收集器管理的区域。
1. 分代收集理论
根据对象命令周期的不同将堆内存区域分成多个不同的区域,以便更高效的进行垃圾回收。
大部分的虚拟机主要分为三个部分:
- 新生代:新创建的对象首先被分配在这里。
- 年轻代又细分为一个Eden区和两个Survivor区(通常称为From和To)。
- 大部分对象在Eden区生成,并且很快成为垃圾。
- 存活下来的对象会通过Minor GC(轻量级垃圾收集)移动到其中一个Survivor区。
- 之后在两个Survivor区间来回移动直至达到一定年龄阈值,最后被晋升到老年代。
- 老年代:用于存放那些在年轻代中经历了多次垃圾回收仍然存活的对象。
- 这些对象被认为是长期存活的,因此被移到老年代中。
- 老年代的空间相对较大,垃圾回收的频率比年轻代低得多,但每次回收的成本更高,这种垃圾回收被称为Major GC或Full GC。
- 永久代/永久代:在Java 8之前,这个区域用于存储类的元数据、方法信息等,称为永久代。
- 从Java 8开始,永久代被移除,取而代之的是元空间(Metaspace),它直接使用本地内存而不是堆内存。
- 元空间主要用于存储类的结构如运行时常量池、字段和方法数据以及方法和构造函数的代码等。
2. 垃圾收集算法
标记-清除(Mark-Sweep)算法
- 工作原理:首先从根节点开始遍历对象图,标记所有可达的对象;然后进行一次遍历,清除未被标记的对象。
- 优点:简单直接,不需要额外空间。
- 缺点:会产生内存碎片,导致后续分配大对象时可能出现连续空间不足的问题。
复制(Copying)算法
- 工作原理:将可用内存分为两个等大的区域,每次只使用其中一个。当需要进行垃圾回收时,将存活的对象复制到另一个区域,然后清空当前使用的区域。
- 优点:解决了内存碎片问题,实现简单高效。
- 缺点:内存利用率较低,因为每次只能使用一半的内存。
标记-整理(Mark-Compact)算法
- 工作原理:类似于标记-清除,但在清除未标记对象之后,会将剩余对象移动到内存的一端,使空闲空间集中在另一端。
- 优点:避免了内存碎片问题,同时提高了内存利用率。
- 缺点:相比单纯的标记-清除,增加了对象移动的成本。
3. 垃圾收集器
Serial /ˈsɪəriəl/ 收集器
- 工作原理:使用单线程进行垃圾回收,执行时会暂停所有其他工作线程(Stop-The-World)。
- 优点:简单高效,适合于单核处理器或小型应用。
- 缺点:在多核处理器上性能不佳,因为只能利用一个CPU核心。
ParNew收集器
- 工作原理:是Serial收集器的多线程版本,适用于新生代的垃圾收集。
- 优点:能够并行处理,适合多核环境;可以与CMS收集器配合使用。
- 缺点:依然存在Stop-The-World事件,在某些情况下可能不如CMS效率高。
Parallel Scavenge /ˈpærəlel/ 收集器
- 工作原理:专注于提高吞吐量,采用多线程进行新生代垃圾收集。
- 优点:通过增加吞吐量来减少GC总时间,特别适合批处理任务。
- 缺点:较高的停顿时间,不适合对响应时间敏感的应用。
Serial Old收集器
- 工作原理:Serial 的老年代版本,使用标记-整理算法。
- 优点:适用于客户端模式下的虚拟机。
- 缺点:仅限于单线程操作,不适合服务器端应用。
Parallel Old收集器
- 工作原理:Parallel Scavenge的老年代版本,支持多线程并行收集。
- 优点:提高了老年代的回收效率,适合注重吞吐量的应用。
- 缺点:仍然会有较长的停顿时间。
CMS(Concurrent Mark Sweep)收集器
- 工作原理:旨在减少停顿时间,采用 标记-清除算法,并尽可能地并发运行。
- 优点:低延迟,适合需要快速响应的应用程序。
- 缺点:消耗更多CPU资源,可能导致碎片化问题,偶尔会出现Full GC导致长时间停顿。
G1(Garbage First)收集器
- 工作原理:将堆分为多个Region,根据各区域垃圾数量优先清理含最多可回收垃圾的区域。支持并行和并发操作,且具备压缩功能以避免内存碎片。
- 优点:预测可控的停顿时间,适合大型堆。
- 缺点:复杂度较高,额外的维护成本可能会占用较多的内存。
ZGC(Z Garbage Collector)
- 工作原理:设计用于大内存堆的低延迟垃圾收集器,采用了读屏障、染色指针等技术实现并发操作,尽量减少停顿时间。
- 优点:极低的停顿时间,即使对于非常大的堆也能有效管理。
- 缺点:需要更多的CPU资源,并且在早期版本中被认为是实验性质的。
三、虚拟机栈
主要作用
JVM虚拟机栈是用于存储方法执行时的局部变量、操作数栈、动态链接、方法出口等信息的内存区域。
虚拟机栈遵循 First In Last Out的操作原则,操作元素是栈帧。
当方法执行的时候会创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
每一个方法执行开始和结束都对应一个栈帧的入栈和出栈。
四、本地方法栈
主要作用
JVM本地方法栈是为执行本地方法(Native方法)提供支持的内存区域。
本地方法栈主要用于存储本地方法调用时的栈帧信息,确保Java程序能够与用其他语言编写的功能模块进行交互。
五、程序计数器
主要作用
程序计数器用于存放当前线程所执行的字节码指令地址。
程序计数器是实现多线程并发的基础。
如果当前线程执行的是本地方法,那么存储的就是空。
通常情况下程序计数器只需要存储字节码地址所需要的空间即可,因此程序计数器也是《Java虚拟机规范》中没有规定任何 OutOfMemoryError (OOM)情况的地方。
六、直接内存
直接内存也称堆外内存,不是JVM规范的一部分,不受JVM堆大小限制,是Java通过本地方法创建的,也会出现 OOM 错误。