数组

JVM 内存

JVM 虚拟机在运行时, 会占用一块内存空间. 为了更好地利用这一块内存空间, 虚拟机把这块空间分成了 5 个部分. 每个部分都有其各自的作用.


图 1

在 JDK7 之前, JVM 将方法区和堆放在一起, 在真实的物理内存中, 也是一块连续的空间. 但是这种设计方式不太好 (为什么不好? 有何影响? 需要在虚拟机内存中学习)

从 JDK8 开始, 取消方法区, 新增元空间, 和堆空间分开了. 把原来的方法区的多种功能进行拆分, 有的功能放到了堆中, 有的功能放到了元空间中.

栈: 方法运行时使用的内存, 比如 main 方法运行时会先进栈. 基本数据类型都放在栈中. 方法执行完毕后就会出栈.

寄存器: 给 CPU 使用, 和我们开发无关.

本地方法栈: JVM 在使用操作系统功能的时候使用, 和我们开发无关.

方法区: 存储可以运行的 class 文件. 当一个类要运行时, 要把这个类的字节码文件, 即 class 文件加载到方法区中临时存储.

堆: 存储对象或者数组, 只要是 new 出来的东西, 比如数组, 都存储在堆内存. new 出来的东西会在堆内存中开辟空间并产生地址, 表示在堆内存中的位置.


图 2

方法进栈后, 有一块属于这个方法的空间, 每一个在这个方法中定义的且应该出现在栈内存中的内容都会出现在这个方法的空间中. 比如图 2 中, 变量 a, b 和 c 都是定义在 main() 方法中的, 且这三个变量就应该出现在栈中, 所以这三个变量都出现在 main() 方法的空间中. 最后的 sout(c); 语句也是在 main() 方法中执行的.

数组的静态初始化可以有简化的书写方式, 这种书写方式是没有 new 关键字的, 但是还是认为是有 new 关键字的, 只是省略没写, 所以还是 new 出来的东西.


图 3 数组的内存图

int[] arr = new int[2]; 这一条语句是由左右两个部分组成的. 等号左边是 int[] arr, 是在栈里面定义了一个变量, 变量名为 arr, 类型限定为 int[], 表示变量 arr 可以记录 int 类型数组的地址值. 等号右边 new int[2] 有 new 关键字, 所以是在堆内存中开辟了一块小空间. 堆内存中的内容是有地址值的, 会通过等号即赋值运算符把地址值赋值给左边的变量 arr, 于是变量 arr 也可以通过这个地址值找到堆内存中的这块小空间. 数组 int[] arr 刚开始创建的时候, 两个元素都默认是 0, 所以执行语句 sout(arr[0]); 和 sout(arr[1]); 时输出的都是 0. 执行到语句 arr[0] = 11; 和 语句 arr[1] =22; 时, 将默认的值修改了. 再往下, 执行语句 sout(arr[0]); 和 sout(arr[1]); 时输出的都是修改之后的值了. 执行语句 int[] arr2 = {33, 44, 55}; 时, 直接将元素默认值 0 改为了指定的值.

在栈内存中的基本类型的变量存储的都是真实的值, 而引用类型如数组名, 存储的是堆内存中的地址值. 所以在用 sout 打印数组名这个变量时, 输出的是地址值.


图 4 两个数组指向同一个空间的内存图

arr1 和 arr2 指向了堆内存中的同一块小空间.


图 5 两个数组指向同一个空间的内存图

图 6 两个数组指向同一个空间的内存图

图 7 数组的内存图

int[] arr1 中的 int[] 表示 arr1 可以存放 int 型数组的地址.

当两个数组指向堆内存中的同一个小空间时, 如果其中一个数组改变小空间中的值, 那么其他数组再次访问该堆内存时, 得到的就是修改之后的值了.

二维数组的内存图

public static void main(String[] args) {
    int[][] arr = new int[2][3];
}

arr 是变量名.

int[][] 是类型, 表示 arr 可以记录 int 类型的二维数组的地址值.


图 1

图 2

需要手动创建一维数组.


图 3

图 4
posted @ 2024-08-31 22:50  有空  阅读(45)  评论(0)    收藏  举报