Java一维数组初始化本质是栈建引用、堆分空间:声明int[] arr仅在栈创建null引用,初始化new int[5]才在堆分配含length头部和元素的连续内存。

Java里一维数组变量初始化,本质是“栈上建引用、堆上分空间”。声明时只在栈中留个空位(值为null),真正分配内存发生在初始化那一刻——JVM在堆中划出连续区域,存元素+长度信息,再把堆首地址填回栈里的引用变量。
声明和初始化要分开看
写 int[] arr; 只是在栈中创建一个名为 arr 的引用变量,此时它不指向任何地方,值是 null。这步不占堆内存,也不存数据。
只有执行初始化操作,才会触发堆内存分配:
-
int[] arr = new int[5];→ 堆中分配 5×4=20 字节连续空间,所有元素默认为 0,头部记录length = 5 -
int[] arr = {1, 2, 3};→ 同样在堆中分配 3×4=12 字节,直接填入指定值,length = 3
堆内存布局有固定结构
数组对象在堆中不是光存元素那么简单。它包含两部分:
- 头部元数据:存放
length值(只读,不可修改) - 数据区:从紧接头部之后开始,连续存放所有元素
比如 int[] arr = new int[3];,假设堆首地址是 0x1000,那么:
-
0x1000–0x1003:存储length = 3(通常占 4 字节) -
0x1004–0x1007:arr[0] -
0x1008–0x100B:arr[1] -
0x100C–0x100F:arr[2]
访问 arr[i] 时,JVM 实际计算的是:首地址 + 4 + i × 4(跳过头部,再按索引偏移)。
栈引用只是“路标”,不是数据本身
栈中的 arr 变量本身很小(通常 4 或 8 字节,取决于 JVM 位数),只保存一个地址,比如 0x1000。它不存任何数组内容,也不参与运算。
这意味着:
- 多个引用可指向同一堆数组(如
int[] b = arr;),改b[0]就等于改arr[0] - 当
arr超出作用域(比如方法结束),栈变量消失,但堆数组只要还有其他引用指向它,就不会被回收 - 堆中无引用的对象,才由 GC 标记并清理——和栈变量生命周期完全解耦
常见误区提醒
容易混淆的点:
-
arr.length不是方法调用,而是直接读取堆头部的字段,所以极快且无开销 - 数组长度一旦确定就不能变,因为堆空间已固定,
length字段也只读 -
new int[0]也会在堆中分配对象(含头部),只是数据区长度为 0;null则连堆对象都没有 - 基本类型数组(如
int[])元素直接存在堆数据区;引用类型数组(如String[])存的是引用地址,实际对象在别处(可能常量池或堆其他位置)










