Java数组变量在栈中仅存地址,数组本体强制在堆中连续存储;声明int[] arr;只分配栈引用槽位(初值null),初始化arr = new int[5];才在堆中创建含对象头、length字段和数据区的完整对象。

数组变量在栈中只存地址
声明 int[] arr; 这行代码本身不创建数组,只是在当前方法的栈帧里分配一个引用槽位(通常4或8字节),初始值为 null。它不占用堆空间,也不含任何数据。只有执行 arr = new int[5]; 后,JVM 才在堆中划分一块连续内存,并把这块内存的起始地址填进栈里的那个槽位。
数组对象本体强制在堆中连续存储
所有 Java 数组都是对象,必须分配在堆内存。这块堆空间包含三部分:对象头(含 GC 标记、类元信息)、4 字节的 length 字段、紧随其后的数据区。例如 new int[3] 实际占用约 28 字节(12+4+3×4),且这 28 字节物理地址连续。JVM 规范强制如此,不是优化选项,也不是可选行为。
基本类型数组与引用类型数组的数据区差异
两者都在堆上,但数据区内容不同:
- int[] 的数据区直接存放数值,如 new int[]{1,2,3} → 堆中存的就是 1、2、3 这三个整数
- String[] 的数据区存放的是引用,每个元素可能是 null,也可能指向堆中其他位置的 String 实例
例如 String[] s = new String[2]; s[0] = "a";,堆中存在两个独立对象:一个是长度为 2 的数组对象,另一个是字符串 "a" 实例,s[0] 存的是后者的地址。
立即学习“Java免费学习笔记(深入)”;
传参时修改元素与重赋引用的区别
向方法传递数组,本质是传递引用的副本(即地址拷贝):
- 执行 arr[i] = x; → 修改的是堆中同一块内存,原数组立即可见变化
- 执行 arr = new int[10]; → 仅改变形参引用所指的位置,原变量仍指向旧数组
- 同理,int[] a = b; 让 a 和 b 指向同一块堆内存;后续任一变量修改元素,对方立刻可见
这种行为源于“栈存地址、堆存实体”的固定分工,不是语言特性,而是 JVM 内存模型的底层事实。


















