Runtime Data Area(运行时数据区域)

运行时数据区是JVM用来管理和存储Java程序执行过程中所需要的各种数据的区域,也成为JVM内存结构。JVM内存结构主要包括以下几个部分:

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

程序计数器是一种特殊的内存区域,它可以在物理机器的寄存器中实现,也可以在虚拟机栈中实现。程序计数器是JVM中的一个非常重要的组成部分,它用于存储当前线程所执行的字节码指令的地址。当JVM在执行Java方法时,程序计数器用于记录当前执行的位置,以便下一次继续执行。

程序计数器是线程私有的,每个线程都有一个独立的程序计数器,因此可以避免多线程之间的干扰和竞争。程序计数器的容量比较小,一般为32位或64位,它的值存储在线程私有的虚拟机栈中。

  1. Java虚拟机栈(Java Virtual Machine Stacks)

栈内存是用于存储栈帧的内存空间,栈帧中包含操作数栈、方法出口和局部变量表等信息。每个线程都有自己的栈内存,栈内存的大小是固定的(默认1MB),在编译时就已经确定,且一旦分配无法改变。当一个方法被调用时,JVM会为该方法分配一块内存空间(栈帧),方法执行结束后,该空间会被释放。由于栈内存是线程私有的,因此不会出现线程安全问题。

局部变量表中存储方法内部声明的变量,操作数栈中存储参与运算或者赋值操作的值,可以是各种类型的数据,例如整型、浮点型、对象引用等。方法出口指的是返回地址,在一个方法执行完毕后需要返回到哪个方法继续执行的地址。

public class JVMStacksDemo {

    static void methodA() {
        int a = 0;
        int b = 10;
        methodB(a, b);
    }

    static int methodB(int a, int b) {
        int x = 100;
        int c = a + b + x;
        String str = "str";
        return c;
    }

    public static void main(String[] args) {
        int a = 20;
        int b = 20;
        methodA();
    }

}

main方法被调用时,JVM会为其创建一个栈帧,并将该方法的返回地址压入栈顶。该栈帧中包含了main方法的局部变量表和操作数栈。main方法中定义了两个整型变量ab,并将ab分别赋值为20。如下图,可以看到当前栈中有main方法的栈帧,且可以看到右侧局部变量表中包含main方法的命令行参数、定义的整型变量ab以及它们的值。

Runtime Data Area(运行时数据区域)_第1张图片
接着main方法调用methodA

此时,JVM会为methodA方法创建一个新的栈帧,并将该方法的返回地址压入栈顶。如下图可以看到栈中methodA的栈帧位于栈的顶部。

Runtime Data Area(运行时数据区域)_第2张图片
methodA中调用的methodB,在methodB的局部变量表中可以看到methodA传入的参数ab,定义的变量xc,引用数据类型变量str
Runtime Data Area(运行时数据区域)_第3张图片

当methodB执行完成后,methodB的栈帧出栈。methodA同理。最后main方法执行完毕,栈空间被释放。

请添加图片描述

请添加图片描述
Runtime Data Area(运行时数据区域)_第4张图片

  1. Java堆(Java Heap)

堆内存是Java程序中最大的一块内存,用于存储对象实例和数组。

在JVM启动时,会创建并分配一个初始大小的堆空间,这个大小可以通过JVM参数来指定。当堆空间不足时,JVM会自动扩展堆的大小。堆内存被所有线程共享,因此可能会出现线程安全问题。但通常情况下,Java对象的访问都是通过引用来进行的,因此不需要考虑对象本身的线程安全问题,而是需要考虑对引用的并发访问问题。

  • 堆内存存储的对象实例和数组都需要使用new关键字创建,如果没有使用new关键字,则存储在栈内存中。

  • 对象实例包括String、ArrayList、HashMap等java中的,也包括自定义的。

  • 堆内存还存储对象的实例变量,如Person类的name、age等实例变量。类的静态变量存储在方法区中,它们也是所有线程共享的。

  • 一些基本类型的包装类也是存储在堆内存中,如常用的包装类Integer、Long、Float、Double、Boolean、Byte、Short和Character等。

堆内存在逻辑上划分为新生代和老年代,新生代又分为Eden区、Survivor1区和Survivor2区。新生代主要存储生命周期较短的对象,而老年代则主要存储生命周期较长的对象。
当一个Java对象被创建时,它首先会被分配在Eden区。当Eden区满时,触发Minor GC垃圾回收,将仍然存活的对象复制到Survivor0区或Survivor1区中。当Survivor0区或Survivor1区满时,也会触发Minor GC,将仍然存活的对象复制到另一个Survivor区中。在经过多次GC后,仍然存活的对象会被移动到老年代中。

  1. 方法区(Method Area)

方法区是用于存储被加载的类的相关信息(包括类名、访问修饰符、常量池、字段描述、方法描述等)以及常量池中的字面量、符号引用等内容的内存空间。方法区在逻辑上是堆的一部分。

  • 方法区被所有线程共享,在JVM启动时被创建,JVM销毁时被销毁。JVM会根据实际情况自行调整方法区的大小。

  • 常量池、静态变量在jdk8之后,常量池和静态变量被转移到了元空间(Metaspace)中。

  • 方法区也是垃圾回收的目标之一,主要回收无用的类信息和常量池中的常量。

  1. 本地方法栈(Native Method Stacks)

本地方法是使用 C/C++ 或者其他本地语言编写的方法,它们不会像 Java 方法那样被编译成字节码,而是被编译成本地机器码。本地方法栈和栈内存类似,用于存储本地方法的调用栈和本地方法的局部变量。本地方法栈也是线程私有的。在执行本地方法时,JVM 将会在本地方法栈中创建一个栈帧,用来存储本地方法的参数和局部变量。和 Java 方法栈类似,栈帧由操作数栈和局部变量表组成,它们的作用也和 Java 方法栈一样。

你可能感兴趣的:(java,jvm,java,算法)