jvm运行时的内存结构


​​

1. 方法区(Method Area)

方法区是JVM内存的一部分,用于存储已被JVM加载的类信息、常量、静态变量和即时编译器(Just-In-Time Compiler, JIT)编译后的代码等数据。方法区在Java 8及之前版本中也被称为永久代(Permanent Generation, PermGen),而在Java 8以后,永久代被移除,取而代之的是元空间(Metaspace)。

  • 类信息:方法区保存每个被加载的类的结构信息,包括类名、访问修饰符、字段、方法、接口、父类等。
  • 常量池:每个类文件中的常量池(如字符串字面量、数值常量)在加载时会被放入方法区的运行时常量池中。
  • 静态变量:方法区还存储类的静态变量,静态变量随着类的加载而分配,随着类的卸载而销毁。
  • 即时编译后的代码:JIT编译器将一些热代码(执行频繁的代码)编译成本地机器码,这些机器码也存储在方法区中。

注意:方法区是所有线程共享的内存区域。

2. 堆(Heap)

是JVM中最大的一块内存区域,用于存放所有对象实例和数组。堆是由所有线程共享的,并且在Java程序运行时从JVM启动开始一直存在。

堆内存通常被进一步划分为两个主要的区域:

  • 新生代(Young Generation):主要存储新创建的对象,新生代又分为三个部分:Eden空间、From Survivor空间、To Survivor空间。新对象一般先分配到Eden空间,当Eden空间被填满时,触发轻度垃圾回收(Minor GC),将还存活的对象移动到Survivor空间。
  • 老年代(Old Generation):存储那些从新生代中晋升(对象多次在新生代垃圾回收后仍然存活)或者生命周期较长的对象。老年代的空间比较大,存放的对象也比较稳定。只有在老年代的空间不足时,才会触发较重的垃圾回收(Major GC或Full GC)。

堆是Java垃圾回收机制的主要工作区域,不再使用的对象由垃圾回收器自动回收。

3. 栈(Stack)

Java虚拟机栈是JVM为每个线程创建的一块内存区域,栈内存在线程创建时分配,线程结束时销毁。每个线程都有自己的虚拟机栈,用来存储局部变量表、操作数栈、动态链接、方法出口等信息。

  • 局部变量表:存储方法中定义的局部变量,包括基本数据类型(如int、float、double等)以及对象引用(Reference类型),局部变量表的大小在编译期确定,并在方法调用时分配。
  • 操作数栈:操作数栈是方法执行时的一个工作区,用于临时存储计算过程中产生的中间结果。
  • 动态链接:每个栈帧中包含一个指向当前方法所在的类的运行时常量池的引用,用于实现方法调用中的符号引用转为直接引用。
  • 方法返回地址:当一个方法执行完毕后,需要返回到调用该方法的位置,这个返回地址也保存在栈中。

栈中的数据是栈帧的形式存在的,每个方法在执行时都会创建一个新的栈帧,方法执行完毕后,栈帧会被弹出并销毁。

4. 程序计数器(Program Counter Register)

程序计数器是一个小的内存区域,可以看作是当前线程所执行字节码的行号指示器。JVM通过改变程序计数器的值来选择下一条需要执行的指令,是控制程序流程的关键组件。

  • 线程私有:程序计数器是每个线程私有的,每个线程在不同时间段执行的代码可能不同,因此每个线程都有一个独立的程序计数器。
  • 字节码解释器的工作基础:字节码解释器通过改变程序计数器的值来读取下一条需要执行的字节码指令。

当执行的是本地方法时,程序计数器值为空(Undefined)。

5. 本地方法栈(Native Method Stack)

本地方法栈与Java虚拟机栈类似,只不过本地方法栈是为本地方法服务的。当Java程序调用本地方法(如C语言编写的代码)时,本地方法栈会保存这些方法的信息。

  • 线程私有:本地方法栈与虚拟机栈一样是线程私有的。
  • 支持本地方法执行:本地方法栈用于保存本地方法的调用信息,包括局部变量、操作数栈、动态链接、返回地址等。

本地方法栈在某些情况下可能与虚拟机栈合并,但在实际应用中,JVM为本地方法栈和虚拟机栈分配独立的内存区域。

6. 运行时常量池(Runtime Constant Pool)

运行时常量池是方法区的一部分,但有着自己独特的作用。它是每个类或接口的常量池表的运行时表示,主要存储编译期生成的字面量和符号引用,以及在运行时可能会添加的新常量。

  • 字面量:如字符串常量、数值常量等。
  • 符号引用:包括类或接口的全限定名、字段名称和描述符、方法名称和描述符等。

在类加载的过程中,JVM将把字节码文件中的常量池信息加载到运行时常量池中,符号引用被解析为直接引用,之后程序才能正确地执行。

7. 直接内存(Direct Memory)

直接内存并不是JVM运行时内存的一部分,而是Java通过java.nio包中的 ByteBuffer 类来操作的内存区域。这部分内存直接分配在操作系统中,可以在某些场景下提高I/O性能,因为它减少了Java堆内存和本地内存之间的数据复制。

直接内存的大小受操作系统限制,但可以通过启动参数-XX:MaxDirectMemorySize进行调节。

总结

JVM运行时内存结构包含了多个部分,每个部分在Java程序的执行中扮演着重要角色:

  • 方法区:存储类信息、常量、静态变量、即时编译后的代码。
  • :存放所有对象实例和数组,是垃圾回收的主要区域。
  • :每个线程都有自己的栈,存储局部变量和方法调用信息。
  • 程序计数器:保存当前线程执行的字节码指令地址。
  • 本地方法栈:支持本地方法的调用。
  • 运行时常量池:方法区的一部分,存储类的常量池信息。
  • 直接内存:由Java通过ByteBuffer类直接操作的内存区域。

了解这些内存区域及其工作原理对于优化Java应用程序的性能、排查内存问题以及进行垃圾回收调优具有重要意义。

猜你喜欢

转载自blog.csdn.net/Flying_Fish_roe/article/details/143445120