Java二维数组的内存使用量

本文对比了一维和二维byte数组在Java中的内存占用情况及访问速度。通过具体实验,分析了两种数组的不同实现方式及其优缺点,特别是针对内存占用进行了深入探讨。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们有这样一个应用:维护一个二维byte大数组,一维的长度是固定的,第二维的数组
可能会修改。
byte[][] bytes=new byte[M][];
for(int i=0;i<bytes.length;i++){
    bytes[i]=new byte[N];
}

我需要访问每个数组,比如bytes[10]
我也需要修改某个数组,比如bytes[10]=new byte[N];
这是一种实现方式。
另一种方式是用一维数组来模拟
long[] pointers=new long[M];
byte[] data=new byte[M*N];
这种方式的优点是节省内存,并且访问速度快。
缺点是无法修改其中的某个数组,只能修改整个data数组。

我做实验测试了一下,

第一种方法使用内存 1.3g,时间11626ms
第二种方法使用内存 740M,随机初始化时间1829ms

==========================一维数组================================
	public static void main(String[] args) {
		if(args.length!=2){
			System.out.println("Usage: java Test2 docNum len");
		}
		int docNum=Integer.valueOf(args[0]);
		int len=Integer.valueOf(args[1]);
		System.out.println("docNum="+docNum+",len="+len);
		long start=System.currentTimeMillis();
		long id=0;
		byte[][] bytes=new byte[docNum][];
		for(int i=0;i<docNum;i++){
			bytes[i]=new byte[len];
			for(int j=0;j<len;j++){
				bytes[i][j]=(byte)(id%128);
				id++;
			}
		}
		System.out.println(id);
		long end=System.currentTimeMillis();
		System.out.println("Test2 timeUsed: "+(end-start)+" ms");
		System.out.println("Create timeUsed: "+(end-start)+" ms");
		start=System.currentTimeMillis();
		for(int i=0;i<bytes.length;i++){
			for(int j=0;j<bytes[i].length;j++){
				id=bytes[i][j];
			}
		}
		end=System.currentTimeMillis();
		System.out.println("Read timeUsed: "+(end-start)+" ms");
		System.gc();
		try {
			Thread.sleep(300000);
		} catch (InterruptedException e) {

		}
		System.out.println(bytes.length);
	}

}


=================================== 二维数组=====================================
	public static void main(String[] args) {
		if(args.length!=2){
			System.out.println("Usage: java Test docNum len");
		}
		int docNum=Integer.valueOf(args[0]);
		int len=Integer.valueOf(args[1]);
		System.out.println("docNum="+docNum+",len="+len);
		long start=System.currentTimeMillis();
		long[] pointers=new long[docNum];
		byte[] data=new byte[len*docNum];

		for(int i=0;i<pointers.length;i++){
			pointers[i]=1L*i*len;
		}
		long id=0;
		for(int i=0;i<data.length;i++){
			data[i]=(byte)(id%128);
			id++;
		}
		System.out.println(id);

		long end=System.currentTimeMillis();
		System.out.println("Create timeUsed: "+(end-start)+" ms");
		start=System.currentTimeMillis();
		for(int i=0;i<data.length;i++){
			id=data[i];
		}
		end=System.currentTimeMillis();
		System.out.println("Read timeUsed: "+(end-start)+" ms");

		try {
			Thread.sleep(300000);
		} catch (InterruptedException e) {

		}
		System.out.println(data.length);
	}
}

读取的时候第二种稍微比第一种快一些,但是比较难以理解的是内存的差别为什么这么大。

一维数组jmap看的结果为:

 num     #instances         #bytes  class name
----------------------------------------------
   1:           413      600079280  [B
   2:             2      160000064  [J
   3:          5204         522520  <constMethodKlass>
   4:          8516         453200  <symbolKlass>
   5:          5204         419104  <methodKlass>

二维数组的结果为:

 num     #instances         #bytes  class name
----------------------------------------------
   1:      20000411      960079232  [B
   2:             1       80000016  [[B
   3:          5204         522520  <constMethodKlass>
   4:          8518         453240  <symbolKlass>
   5:          5204         419104  <methodKlass>
   6:           343         200296  <constantPoolKlass>
   7:          1505         198464  [C

一维数组的大小比较好解释:20M*30=600M的byte数组,然后加上20M*8(Long)

二维数组在java里是20M个一维数组,除了数据的20M*30外,多了20M个引用,也就是80M的空间(在64bit机器160M)

怎么会多出这么多呢? 1: 20000411 960079232 [B 平均一个一维数组时48个字节,但是我们的大小只有30个字节,然后加上数组会保持数组的长度4个字节。也就34个字节,怎么会这么多呢?
后来查了一下,原来每个对象都会保持8个字节的头部信息,jvm用它来管理内存。这样就是42个字节,也不是48个字节啊。后来又搜索了一些,发现HotSpots会对对象的空间做字节对齐,
这样就变成48个字节了。为了验证这个想法,把二维数组从30个byte变成34个byte,使用的空间没有变化。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值