Jvm内存分布

JVM内存分配包含五块内存区域,分别是虚拟机栈、本地方法栈、程序计数器,这三块区域是线程私有的不必担心数据安全性问题,以及Java堆和方法区:

Java存储树到数据库 java数据存储实例_程序计数器

程序计数器,每个线程都会有自己的程序计数器记录其执行到哪一行代码,是当前线程执行的字节码指示器。在多线程运行环境下,当前线程如果没有程序计数器进行记录执行位置,那么下一次切回当前线程就无从得知从哪一行代码开始执行。

虚拟机栈,Java类中一个方法的执行对应着一个虚拟机栈帧的入栈和出栈,栈帧对应着一次方法运行时需要暂存的相关数据,栈帧包含局部变量表、操作数栈、动态链接、方法的返回地址、其他信息。局部变量表存放该方法中定义的变量、引用等数据,操作数栈存储方法中使用的基本数据类型,动态链接特指方法运行时将运行时常量池中符号引用转换为指向该方法地址的直接引用(以便随后的方法调用),方法返回地址包括方法的正常返回以及异常抛出,其他信息譬如栈帧深度等信息也需要记录。

本地方法栈,JNI方法调用使用到的内存区域。

堆,类实例存储的区域,占用JVM的大部分空间,同时也是GC收集的主要区域。
方法区,存放JVM加载的类信息、常量、静态变量、即时编译后的代码等数据。

Java数据存储区域分析

下面来看一个代码示例:

public class Analysis {
    private int statefulVar;

    public Analysis(int statefulVar) {
        this.statefulVar = statefulVar;
    }
    
    public int method(boolean flag) {
        int a = 0;
        if (flag) {
            throw new ArrayIndexOutOfBoundsException();
        }
        return 1;
    }

    public static void main(String[] args) {
        Analysis instance = new Analysis(1);
        instance.method(true);
    }
}

上面这段java代码在经过编译后生成class字节码文件被加载进方法区,首先分析main函数的执行过程:

main函数由主线程执行,当执行到new Analysis(1)时,会在堆中生成一个实例对象,该对象包含对象头、对象实例数据(statefulVar存储地址由方法区中该字段符号引用指定)、字节填充(填充为8字节的倍数)。对象实例数据存储的Analysis中定义的statefulVar,并在虚拟机栈中压入一个栈帧,该栈帧包含的数据有局部变量表记录instance信息,然后执行instance.method(true),对象的method()方法会存在方法区中,instance.method(true)的执行会压入一个新的栈帧到虚拟机栈中,虚拟站中栈顶的栈帧代表当前前程正在执行的方法。

继续分析method()方法,局部变量表包含a、flag,操作数栈包含0,方法返回地址为异常抛出位置和方法正常返回地址;方法返回后该栈帧将被虚拟机栈弹出,此时栈顶为main()方法,最后main()方法执行完毕,栈帧弹出。