4.6 深入数组 4.6.1 内存中的数组

4.6 深入数组

数组是一种引用数据类型,数组引用变量只是一个引用,数组元素和数组变量在内存里是分开存放的。下面将深入介绍数组在内存中的运行机制

4.6.1 内存中的数组

数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内在后,才可通过该数组变量来访问数组元素。
与所有引用变量相同的是,引用变量是访问真实对象的根本方式。也就是说,如果希望在程序中访问数组对象本身,则只能通过这个数组的引用变量来访问它。

数组对象存储在堆内存中

实际的数组对象被存储在堆(heap)内存中;如果引用该数组对象的数组引用变量是一个局部变量,那么它被存储在栈(stack)内存中。数组在内存中的存储示意图如图4.2所示:
这里有一张图片
如果需要访问如图4.2所示堆内存中的数组元素,则程序中只能通过p[index]的形式实现。也就是说,数组引用变量是访问堆内存中数组元素的根本方式

栈内存 堆内存

方法中定义的局部变量保存在占栈内存

当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈存里。

方法结束 栈内存自动销毁

随着方法的执行结束,这个方法的内存栈也将自然销毁。
因此,所有在方法中定义的局部变量都是放在栈内存中的;

对象存储在堆内存

在程序中创建一个对象时,因为对象的创建成本通常较大,所以将对象保存到运行时数据区中,以便反复利用,这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(在方法的参数传递时很常见),则这个对象依然不会被销毁。

对象由垃圾回收器回收

只有当一个对象没有任何引用变量引用它时,系统的圾回收器才会在合适的时候回收它

如果不再有任何引用变量指向堆内存中的数组,则这个数组将成为垃圾,该数组所占的内存将会被系统的垃圾回收机制回收。因此,为了让垃圾回收机制回收一个数组所占的内存空间,可以将该数组变量赋为null,也就切断了数组引用变量和实际数组之间的引用关系,实际的数组也就成了垃圾。

改变数组引用变量的指向

只要类型相互兼容,就可以让一个数组变量指向另一个实际的数组,这种操作会让人产生数组的长度可变的错觉。
如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ArrayInRam
{
public static void main(String[] args)
{
// 定义并初始化数组,使用静态初始化
int[] a = {5, 7 , 20};
// 定义并初始化数组,使用动态初始化
int[] b = new int[4];
// 输出b数组的长度
System.out.println("b数组的长度为:" + b.length);
// 循环输出a数组的元素
for (int i = 0 ,len = a.length; i < len ; i++ )
{
System.out.println(a[i]);
}
// 循环输出b数组的元素
for (int i = 0 , len = b.length; i < len ; i++ )
{
System.out.println(b[i]);
}
// 因为a是int[]类型,b也是int[]类型,所以可以将a的值赋给b。
// 也就是让b引用指向a引用指向的数组
b = a;
// 再次输出b数组的长度
System.out.println("b数组的长度为:" + b.length);
}
}

运行上面代码后,将可以看到先输出b数组的长度为4,然后依次输出a数组和b数组的每个数组元素,接着会输出b数组的长度为3。看起来似乎数组的长度是可变的,但这只是一个假象。必须牢记:**定义并初始化一个数组后,在内存中分配了两个空间,一个用于存放数组的引用变量,另一个用于存放数组本身
下面将结合示意图来说明上面程序的运行过程:

当程序定义并初始化了a、b两个数组后,系统内存中实际上产生了4块内存区,其中栈内存中有两个引用变量:a和b。
堆内存中也有两块内存区,分别用于存储a和b引用所指向的数组本身。此时计算机内存的存储示意图如图4.3所示:
这里有一张图片
从图4.3中可以非常清楚地看出a引用和b引用各自所引用的数组对象,并可以很清楚地看出a变量所引用的数组长度是3,b变量所引用的数组长度是4。

当执行代码b=a;时,系统将会把a的值赋给b,ab都是引用类型变量,存储的是地址。
因此把a的值赋给b后,就是让b指向a所指向的地址。此时计算机内存的存储示意图如图4.4所示:
这里有一张图片
从图4.4中可以看出,当执行了b=a;之后,堆内存中的第一个数组具有了两个引用:a变量和b变量都引用了第一个数组。此时第二个数组失去了引用,变成垃圾,只有等待垃圾回收机制来回收它,但它的长度依然不会改变,直到它彻底消失.