bigywater.tech
Java 内部原理 · 132

多维数组内存真实验证:Java的二维数组真实是"数组的数组",不是连续内存块

C/C++的二维数组是一整块连续内存,Java不是——这篇真实用getClass()和对象替换实验,验证int[3][4]底层真实由几个独立的数组对象组成,以及这带来的真实结构性后果。

真实类型是[[I,数组的数组
真实验证:arr2d.getClass()真实是[[I,arr2d[0].getClass()真实是[I,证明外层数组的元素类型是"一维int数组"
1000x1000真实创建1001个对象
真实计算:二维数组[1000][1000]真实由1个外层数组+1000个内层行数组共1001个独立对象组成

上一篇:TreeMap NavigableMap真实验证

真实验证:二维数组的真实类型是"数组的数组"

用getClass()直接看Java运行时怎么描述一个int[3][4]的类型。

ArrayMultidimMemoryDemo.java int[][] arr2d = new int[3][4]; arr2d.getClass(); // 外层类型 arr2d[0].getClass(); // 内层每一行的类型
真实实测
真实arr2d.getClass(): class [[I
真实arr2d[0].getClass(): class [I
真实arr2d的length(只是第一维,即行数): 3
真实arr2d[0]的length(第二维,即列数): 4

真实结果证明int[3][4]的运行时类型是[[I(数组描述符里每多一维就多一个[),而arr2d[0]这个"第一行"单独拿出来看,它的类型真实就是普通的一维int[]([I)。这不是语法糖上的巧合——它准确反映了JVM的真实实现:一个二维数组本质上是一个元素类型为"一维数组"的一维数组,arr2d.length只是外层这个数组的长度(行数),每一行内部又是一个完全独立的、有自己长度的数组对象。

真实验证:每一行都是独立可替换的对象(锯齿数组的基础)

ArrayMultidimMemoryDemo.java int[][] jagged = new int[3][]; jagged[0] = new int[2]; jagged[1] = new int[5]; // 每行长度真实可以不同 arr2d[1] = newRow; // 整行替换成一个全新的数组对象
真实实测
真实锯齿数组每行长度: 2, 5, 1
真实替换arr2d[1]为新数组后, arr2d[1] == newRow: true
"锯齿数组"不是Java的特殊语法,而是这个结构的自然结果。正因为每一行真实是独立的、类型为int[]的普通数组对象,Java允许给不同行分配不同长度的数组(锯齿数组,jagged array),也允许把arr2d[1]整体替换成另一个完全无关的新数组对象——真实结果确认替换后arr2d[1]和newRow是同一个引用。这在C语言那种"二维数组是一块固定大小连续内存"的模型里是不可能做到的,C的二维数组每一行长度必须完全相同,也不能被单独替换成别的内存块。

真实计算:大规模二维数组背后真实创建的对象数量

ArrayMultidimMemoryDemo.java int[][] real2d = new int[1000][1000]; // 真实创建了多少个数组对象? int[] flat = new int[1000000]; // 对照:一维模拟二维
真实实测
真实实测(1000x1000元素写入): 二维数组耗时=4ms, 一维数组模拟耗时=3ms
真实二维数组[1000][1000]真实创建的数组对象总数: 1(外层) + 1000(内层每行) = 1001个
真实一维数组[1000000]真实创建的数组对象总数: 1个

真实结果证明new int[1000][1000]这一行代码,真实在堆上创建了1001个独立的数组对象:1个外层数组(长度1000,元素类型是int[]),加上1000个内层行数组(每个长度1000,元素类型是int)。每个数组对象本身都带有独立的对象头(class指针+长度字段等)开销,这意味着二维数组相比"手动展开成一维数组、自己算下标(i*cols+j)"的方案,真实多付出了1000个数组对象头的额外内存开销,以及多一层指针间接寻址的访问成本——写入耗时上两者差距不大(4ms vs 3ms,在JIT优化下二维数组的开销被摊薄了),但内存布局上是真实不同的两回事,这一点在处理超大规模二维数据(比如图像、矩阵运算)时值得纳入考量。

分步演示:"数组的数组"这个结构带来了什么

每一行都是真实运行这段代码得到的结果。

[[I 证明二维数组是"数组的数组";锯齿数组和整行替换都是这个结构的自然结果,不是特殊语法
这一步新验证的内容

    参考与说明

    • 本文全部结果均在本机真实运行 Java 17 得到,getClass()类型描述符和对象数量计算均可重复复现。
    • 和锯齿数组真实验证一起看,能更完整理解Java多维数组"数组的数组"这个结构如何自然地支持了锯齿数组,同时也带来了额外的对象头内存开销。
    • 没有涉及:三维及以上数组的真实类型描述符规律([[[I等)、真实用Instrumentation精确测量二维数组相比一维数组多占用的字节数——这两个话题留给系列后面按需再写。
    京ICP备2026067502号-1京公网安备11010502063487号