【Java】JVM内存模型解析

 

 JVM内存模型主要分为五大区域:栈、堆、本地方法栈、程序计数器、方法区。

 

本地方法栈:

跟虚拟机栈非常相似,也是线程私有的,不过虚拟机栈是针对Java方法,而本地方法栈是针对native方法,也就是底层方法。

由于Java是跨平台语言,导致的它不得不牺牲一些对底层方法的控制,而要实现这些底层方法的控制,就需要用到native方法,而本地方法栈就是针对native方法的。

程序计数器:

每一个线程内部都有自己的程序计数器,是相互独立的,保证每个线程的程序运行不会出错,因此程序计数器是线程私有的。

在idea将Java代码运行时都会被转译成字节码文件,字节码文件是二进制文件,识别起来比较困难,所以在编译时用到计数器,为编译好的字节码添加行号,后期调用的时候就能按顺序分条执行。记录程序运行的位置。

虚拟机栈:

线程私有的,生命周期和线程相同,它描述的是Java方法执行的内存模型,每一个虚拟机栈都有自己的栈针,每一个方法的执行都是入栈和出栈的过程(想到关于栈和队列的区别,有一个形象的例子,队列是先进先出 吃了拉,栈是先进后出 吃了吐 ),每一个栈中都有着局部变量表和操作数栈,局部变量表是变量值的存储空间,用于存放方法参数和方法内部定义的局部变量,操作数栈存储的数据与局部变量表一致,是通过弹栈和压栈来进行访问的,操作数栈可以理解为Java虚拟机栈中的一个用于计算的临时数据存储区。(Java的基本数据类型大部分都是存储在栈的,除了一些引用变量和引用类型。String不是基本据类型!基本数据类型就只有八个,数值型:byte,short,int,long,浮点型:float,double,字符型:char,布尔型:boolean。

方法区:

也叫永久区,jdk8以后叫元数据区,是线程共享,里面存储着被虚拟机加载的类信息,常量,静态变量

程序执行时,将字节码文件加载到方法区的常量池,存储一些和类有关的属性,方法区的清理也是通过垃圾回收,回收目标主要是常量池中废弃的常量和不再使用的类型。

 堆:

堆区用于存放所有new出来的对象和数组,是线程共享的,堆区中的对象和栈区中的对象往往是成对出现的,一般的程序是通过栈区的对象引用来访问堆区的对象,因为是共享的,意味着对个对象引用可以指向同一个对象,在没有引用变量的指向时,就会变成垃圾,不被使用,被垃圾回收机制清理。

 

看一下代码的执行结果。

 public static void main(String[] args) {
        ArrayList<String> a = null;
        test(a);
        System.out.println(a.size());
    }

    public static void test(ArrayList<String> a){
        a = new ArrayList<>();
        a.add("a");
    }

执行后会返回什么?打印出1吗?

结果显然不是,会在 System.out.println(a.size()); 时抛出空指针异常。

 

我们分析下程序执行的内存模型。

首先定义了一个ArrayList类型的a变量,a是一个指针,不过现在指向的是null。

调用了test方法,传入形参a,实际上传的是a的副本。

在静态方法test中,new了一个arrayList,在堆中开辟了空间存储了new的对象,将a指向堆中new出来对象的地址。

然后执行 a.add("a"); 

test方法结束,销毁test方法执行时产生的局部变量。

回到main方法中,打印a.size时的a还是之前声明的指向null的变量。故抛出空指针异常。

 

https://www.bilibili.com/video/BV12t411u726?from=search&seid=1631061805620865620

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章