JVM内存模型
JVM(Java Virtual Machine)内存模型是 Java 程序在运行时,JVM 为其分配的内存结构,它定义了 Java 程序如何在内存中存储数据和如何进行线程之间的通信。JVM 内存模型是为了支持高效的多线程执行和垃圾回收机制。
一、JVM 内存模型的组成部分:
JVM 内存模型可以分为以下几个主要部分:
1.方法区(Method Area):
- 作用:用于存储类信息、常量、静态变量和即时编译器(JIT)编译后的代码等数据。可以认为是JVM内存的一部分,是所有线程共享的区域。
- GC:方法区的回收机制也称为"永久代(Permanent Generation)",但在 JDK 8 后,它被移除,改为使用元空间(Metaspace)。元空间存储类的元数据,不再占用堆内存。
2.堆(Heap):
- 作用:存储 Java 中所有的对象实例和数组,是 Java 内存中最大的一部分,所有线程共享。堆内存是垃圾回收器(GC)的主要工作区域。
- GC:Java的垃圾回收器负责清理堆中的无用对象,从而避免内存泄漏和减少内存占用。
3.栈(Stack):
- 作用:每个线程都有一个独立的栈。栈内存存储方法的局部变量、操作数栈、部分方法的返回地址等信息。每次调用方法时,会创建一个栈帧(stack frame),方法执行结束后,该栈帧会被销毁。
- 局部变量:栈上存储的是方法中的局部变量、对象引用(并不是对象本身)等数据。栈中的内存是线程私有的,不会被其他线程共享。
4.程序计数器(PC寄存器):
- 作用:程序计数器记录的是线程当前执行的字节码指令的地址。每个线程都有一个独立的程序计数器,线程切换时不会丢失执行的上下文信息。
- 特殊性:对于本地方法(Native Method),程序计数器的内容会为空,因为它不执行字节码指令。
5.本地方法栈(Native Method Stack):
- 作用:本地方法栈与栈类似,但它是用于支持本地方法(native methods)的执行。它存储的是调用本地方法(如通过 JNI 调用的 C 代码)的相关信息。
- 区别:与 Java 栈相比,本地方法栈是用于管理本地代码的执行,不处理 Java 字节码。
二、JVM 内存的分配:
1.线程共享与线程私有:
- 堆和方法区是所有线程共享的内存区域。
- 每个线程有自己的栈、程序计数器和本地方法栈。
2.内存分配与回收:
- 在堆中,内存分配通常是由垃圾回收器管理的。随着对象的创建和销毁,垃圾回收器会自动回收不再被引用的对象。
- 栈的内存分配和销毁由方法调用和返回控制。每当方法调用时,栈就会分配一个新的栈帧;方法执行完毕后,该栈帧会被销毁。
三、JVM 内存模型的关键特性:
- 内存模型的并发性:Java 内存模型(JMM)保证了多线程下对共享变量的访问是可见的。JVM 内存模型规定了线程如何通过主内存和工作内存(缓存)共享数据。
- 内存屏障和同步:JVM 内存模型定义了"happens-before"规则,确保了多个线程访问共享变量时的有序性,避免了并发中的内存可见性问题。
四、垃圾回收与内存管理:
JVM 的垃圾回收机制负责自动回收不再使用的对象,常见的垃圾回收算法包括:
- 标记-清除算法:标记所有要回收的对象,然后清除它们。
- 复制算法:将对象分配到不同的区域,然后清理整个区域。
- 分代收集:根据对象存活的时间将堆分为多个区域,分别采用不同的回收策略。
五、内存区域图示:
java
+----------------------------+
| 程序计数器(PC) |
+----------------------------+
| 本地方法栈(Native) |
+----------------------------+
| 栈(Stack) | <--- 每个线程
+----------------------------+
| 堆(Heap) | <--- 所有线程共享
+----------------------------+
| 方法区(Method Area) |
+----------------------------+
六、JVM内存模型通俗易懂的例子:
JVM内存模型的工作方式可以类比为一个厨房或公司的工作环境。我们将JVM的不同内存区域比作不同的区域,每个区域有不同的功能和作用。
1. 方法区(Method Area)
- 类比:厨房的储藏室
- 方法区用于存放所有类的信息,包括类的结构、常量池、静态变量等。
- 这里相当于厨房的储藏室,存放的是所有菜谱(类定义)和厨房用具(静态变量、常量)。
2. 堆(Heap)
- 类比:厨房的锅和食材
- 堆是存放所有对象的地方,所有的Java对象都存放在堆上。
- 类似厨房中的锅和食材,员工(线程)在这里工作并储存自己的食材(对象)。堆是所有线程共享的资源。
3. 栈(Stack)
- 类比:每个员工的工作台
- 每个线程有自己的栈,栈用于存储方法调用的栈帧(局部变量、方法参数等)。
- 就像每个员工有自己的工作台,每个人独立地完成自己的任务,不互相干扰。
4. 程序计数器(Program Counter,PC)
- 类比:员工的任务进度表
- 程序计数器记录当前线程执行的指令位置(即下一个执行的指令)。
- 就像每个员工都有自己的任务进度表,记录他们当前在做什么。
5. 本地方法栈(Native Method Stack)
- 类比:员工在外部执行特殊任务时使用的工具
- 本地方法栈用于执行本地方法(通常是用C/C++编写的代码)。
- 类似于员工在外部做任务时需要的特殊工具或设备。
6、内存区域协作
- 多线程共享资源: 方法区和堆是所有线程共享的,就像厨房中的食材和工具,多个员工(线程)可以同时使用。
- 每个线程独立工作: 每个线程都有自己的栈和程序计数器,就像每个员工有自己的工作台和任务进度表,互不干扰。
7、垃圾回收(GC)
- 类比:清理厨房的无用物品
- 当堆中的对象不再被引用时,JVM会回收这些对象,就像厨房清理掉不再使用的食材或工具。
- 这个过程称为垃圾回收(GC),目的是释放内存空间,让其可以被其他对象使用。
8、小结
JVM内存模型就像一个组织良好的厨房或公司,每个员工(线程)有自己的工作空间(栈和程序计数器),但他们共享一些资源(方法区和堆)。垃圾回收机制确保了不再使用的对象能够被清理,保持内存的高效利用。
七、JVM调优:
八、总结:
总结来说,JVM 内存模型通过合理的内存分配和回收机制,为 Java 程序提供高效的运行环境,并确保多线程并发执行时的内存一致性。