Java基础知识学习 - JVM内存模型和数组的内存分布

JVM内存模型以及数组的内存分布

JVM内存模型

JVM内存区域分为5块,分别是:JVM栈 本地方法栈 堆 方法区 程序计数器

1.JVM栈(stack)

JVM栈用于描述方法调用

其原理是,在方法调用时,将包含方法信息(局部变量/指令/返回值)的 “栈帧” 打入栈中,方法结束时再将 “栈帧” 推出栈。每个方法都有一个独立的 “栈帧”。利用栈 “先进后出” 的特点可以控制方法的执行顺序,被调用的方法先于调用的方法,尤其是 mian 方法,它是第一个方法最先入栈,所以它在栈底,并且最后出栈。

同时这里也牵扯到了局部变量的生命周期,局部变量是方法体中定义的变量,那么局部变量也存储在JVM栈中的**方法“栈帧”**中,方法调用或者方法栈帧入栈时局部变量生效,方法结束或方法栈帧出栈时局部变量失效。

2.本地方法栈

本地方法栈的在作用上和JVM栈是一样的,但是对使用的对象不同。

本地方法是指在Java中被关键字native修饰,由Java程序调用的C/C++语言的功能实现,即非Java代码实现的方法,同时也不具有方法体。

本地方法的目的是可以对操作系统或底层进行操作,这是C/C++的特点之一,而Java对底层不敏感也不可控。

3.堆内存(heap)

堆内存是JVM最主要的内存模型,占用最多的空间,主要为new关键字创建的对象开辟空间,这是因为Java是面向对象语言。特别的,栈中的引用变量就指向堆内存中的地址。

4.方法区

方法区同样和面向对象有关

5.程序计数器

JVM的二进制字节码文件是 ‘逐行’ 解释执行的,程序计数器用于指示逐行的过程。

这五个内存模型中JVM栈和堆内存最为重要,JVM栈决定程序执行的顺序,堆内存决定了程序的存储。

以下给出 方法 - 栈 的内存模型例子

public static void main(String[] args) {
    int a = 1;
    test();
    test3();
}

public static void test() {
    int a = 10;
    test2();
}

public static void test2() {
    int a = 100;
}

public static void test3() {
    int a = 1000;
}

对应的内存情况如下,执行test()语句时:
在这里插入图片描述

执行test3()语句时

在这里插入图片描述

方法test3()的栈帧需要等到方法test()结束时才入栈,即test()出栈后执行

也可以使用IDEA的DEBUG模式可以查看程序执行时当前的栈,如下勾选frames(栈帧):

在这里插入图片描述

可以看到 main() test() test2()的栈帧

在这里插入图片描述

以及 test3() 的栈帧

栈和堆内容上的区别

1.存储类型:栈存储局部变量,包含普通变量和引用变量,引用变量指向堆上的对象。堆存储对象。

2.默认值:栈中存储的是局部变量,而局部变量是没有默认值的。堆中按类型有不同的默认值,如下:

​ byte:0

​ short:0

​ int:0

​ long:0L

​ float:0.0F

​ double:0.0

​ char:\u0000

​ boolean:false

​ 引用数据类型类型:null 表示引用没有指向任何对象,无法对对象进行操作,不可用

3.生命周期:

栈上的局部变量生命周期与方法“同生共死”

堆上对象的生命周期从对象的创建到对象的销毁,对象的创建,目前使用new关键字创建,对象的销毁,当方法结束,引用销毁,对象没有被任何引用指向时,对象成为“垃圾(对象)”,由Java全自动回收对象开辟的空间,这个过程称为GC(garbage collection)

GC是面向对象编程语言对于解决内存泄漏的一种选择,不同于C++的手动回收如free。GC的优点有全自动,易于程序员使用,降低编程门槛,缺点有不确定性和不可控性,程序员最多只能通过System.gc()通知系统需要进行垃圾回收。

数组的内存模型
动态初始化

首先数组是引用数据类型,那么在栈中存储数组的引用,在堆中存储数组对象的内容。

如果直接打印引用,Java会打印出数组对象在堆中的地址,如下:

public void testArray4() {
        int[] arr = new int[3];
        System.out.println(arr);
}

/*
	结果:[I@256216b3
    [ 表示这是一维数组,[[ 则是二维数组
    I 表示这是int类型
    @ 表示后面的十六进制数为地址
*/

在JVM内存模型中表示为:

在这里插入图片描述

静态初始化

同动态初始化差不多,不过元素取值不是默认值

注意:虽然静态初始化元素取值最终不是默认值,但是静态初始化时,元素取值最开始一定是默认值,这与对象的构造函数有关。不过是静态还是动态初始化,都一定有赋值默认值的过程。

两个引用指向同一个对象
int[] arr = new int[3];
int[] arr2 = arr;	//如果数据类型不同,则不能赋值

System.out.println(arr == arr2);	//结果为true

定有赋值默认值的过程。

两个引用指向同一个对象
int[] arr = new int[3];
int[] arr2 = arr;	//如果数据类型不同,则不能赋值

System.out.println(arr == arr2);	//结果为true

在JVM内存中,两个引用指向同一个数组,其中有关引用改变数组,则另一个引用使用时也会发生相应的改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值