一维数组和二维数组存储占用内存大小问题

java1234

共 5501字,需浏览 12分钟

 ·

2020-09-10 02:05

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

66套java从入门到精通实战课程分享 

问题:在java中,一维数组和二维数组在数据量一样的情况下,开辟的内存大小是怎样的?

一、尝试阶段:

1、代码一:

public class OneArrayMemory{  public static void main(String[] args){    int num1 = 1024*1024*2;    int[] arr1 = new int[num1];    for(int i = 0;i < arr1.length;i++){      arr1[i] = i;    }
//获得占用内存总数,并将单位转换为MB long memory1 = Runtime.getRuntime().totalMemory()/1024/1024; System.out.println("用一维数组存储占用内存总量为:"+memory1+"MB");
int nums2 = 1024*1024; int[][] arr2 = new int[nums2][2]; for(int i = 0;i < arr2.length;i++){ arr2[i][0] = i; arr2[i][1] = i; }
//获得占用内存总数,并将单位转换为MB long memory2 = Runtime.getRuntime().totalMemory()/1024/1024; System.out.println("用二维数组存储占用内存总量为:"+memory2+"MB"); }}

2、运行结果:

用一维数组存储占用内存总量为:123MB用二维数组存储占用内存总量为:123MB

 3、结果有悖于常识,百思不得解。后来查阅了资料,发现了了问题所在。下面补充几个知识点:

      最近在网上看到一些人讨论到java.lang.Runtime类中的freeMemory(),totalMemory(),maxMemory ()这几个方法的一些问题,很多人感到很疑惑,为什么,在java程序刚刚启动起来的时候freeMemory()这个方法返回的只有一两兆字节,而随着 java程序往前运行,创建了不少的对象,freeMemory()这个方法的返回有时候不但没有减少,反而会增加。这些人对freeMemory()这 个方法的意义应该有一些误解,他们认为这个方法返回的是操作系统的剩余可用内存,其实根本就不是这样的。这三个方法反映的都是java这个进程的内存情 况,跟操作系统的内存根本没有关系。下面结合totalMemory(),maxMemory()一起来解释。

1)maxMemory()这个方法返回的是java虚拟机(这个进程)能构从操作系统那里挖到的最大的内存,以字节为单位,如果在运行java程序的时 候,没有添加-Xmx参数,那么就是64兆,也就是说maxMemory()返回的大约是64*1024*1024字节,这是java虚拟机默认情况下能 从操作系统那里挖到的最大的内存。如果添加了-Xmx参数,将以这个参数后面的值为准,例如java -cp ClassPath -Xmx512m ClassName,那么最大内存就是512*1024*0124字节。

2)totalMemory()这个方法返回的是java虚拟机现在已经从操作系统那里挖过来的内存大小,也就是java虚拟机这个进程当时所占用的所有 内存。如果在运行java的时候没有添加-Xms参数,那么,在java程序运行的过程的,内存总是慢慢的从操作系统那里挖的,基本上是用多少挖多少,直 挖到maxMemory()为止,所以totalMemory()是慢慢增大的。如果用了-Xms参数,程序在启动的时候就会无条件的从操作系统中挖- Xms后面定义的内存数,然后在这些内存用的差不多的时候,再去挖。

3)freeMemory()是什么呢,刚才讲到如果在运行java的时候没有添加-Xms参数,那么,在java程序运行的过程的,内存总是慢慢的从操 作系统那里挖的,基本上是用多少挖多少,但是java虚拟机100%的情况下是会稍微多挖一点的,这些挖过来而又没有用上的内存,实际上就是 freeMemory(),所以freeMemory()的值一般情况下都是很小的,但是如果你在运行java程序的时候使用了-Xms,这个时候因为程 序在启动的时候就会无条件的从操作系统中挖-Xms后面定义的内存数,这个时候,挖过来的内存可能大部分没用上,所以这个时候freeMemory()可 能会有些大。

结果异常的根源:totalMemory() 减去freeMemory()才是真正给数组开辟的内存大小!!!

4、修改代码

public class OneArrayMemory{  public static void main(String[] args){    int num1 = 1024*1024*2;    int[] arr1 = new int[num1];    for(int i = 0;i < arr1.length;i++){      arr1[i] = i;    }
//获得占用内存总数,并将单位转换为MB long memory1 = Runtime.getRuntime().totalMemory()/1024/1024 - Runtime.getRuntime().freeMemory() / 1024 / 1024; //long memory1 = Runtime.getRuntime().totalMemory()/1024/1024; System.out.println("用一维数组存储占用内存总量为:"+memory1+"MB");
int nums2 = 1024*1024; int[][] arr2 = new int[nums2][2]; for(int i = 0;i < arr2.length;i++){ arr2[i][0] = i; arr2[i][1] = i; }
//获得占用内存总数,并将单位转换为MB long memory2 = Runtime.getRuntime().totalMemory()/1024/1024 - Runtime.getRuntime().freeMemory() / 1024 / 1024; //long memory2 = Runtime.getRuntime().totalMemory()/1024/1024; System.out.println("用二维数组存储占用内存总量为:"+memory2+"MB"); }}

第二次运行结果: 

用一维数组存储占用内存总量为:10MB用二维数组存储占用内存总量为:37MB

5、代码三:

import java.util.Arrays;  public class OneArrayMemory {    public static void main(String[] args) {        long startTime1 = System.currentTimeMillis(); // 获取开始时间        int num1 = 1024 * 1024 * 2;        int[] arr1 = new int[num1];        Arrays.fill(arr1, 1);
// 获得占用内存总数,并将单位转换成MB long memory1 = Runtime.getRuntime().totalMemory() / 1024 / 1024 - Runtime.getRuntime().freeMemory() / 1024 / 1024; System.out.println("用一维数组存储占用内存总量为:" + memory1 + "MB"); long endTime1 = System.currentTimeMillis(); // 获取结束时间 System.out.println("程序运行时间:" + (endTime1 - startTime1) + "ms");
long startTime2 = System.currentTimeMillis(); // 获取开始时间 int num2 = 1024 * 1024; int[][] arr2 = new int[num2][2]; for (int[] i : arr2) { Arrays.fill(i, 1); }
// 获得占用内存总数,并将单位转换成MB long memory2 = Runtime.getRuntime().totalMemory() / 1024 / 1024 - Runtime.getRuntime().freeMemory() / 1024 / 1024; System.out.println("用二维数组存储占用内存总量为:" + memory2 + "MB"); long endTime2 = System.currentTimeMillis(); // 获取结束时间 System.out.println("程序运行时间:" + (endTime2 - startTime2) + "ms"); }
}

运行结果:

用一维数组存储占用内存总量为:10MB程序运行时间:12ms用二维数组存储占用内存总量为:38MB程序运行时间:115ms

二、结论:数据量相同的情况下,二维数组比一维数组需要开辟更大的内存空间。


三、分析

1、一个完整的Java程序运行过程会涉及以下内存区域:

寄存器:JVM内部虚拟寄存器,存取速度非常快,程序不可控制。

:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。

:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。

常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于堆中。

代码段:用来存放从硬盘上读取的源程序代码。

数据段:用来存放static定义的静态成员。

                                

Java中有两种类型的数组:

  • 基本数据类型数组;

  • 对象数组;

2、当一个对象使用关键字“new”创建时,会在堆上分配内存空间,然后返回对象的引用,这对数组来说也是一样的,因为数组也是一个对象;

1)一维数组

int[] arr = new int[3];

在以上代码中,arr变量存放了数组对象的引用;如果你创建了空间大小为10的整形数组,情况是一样的,一个数组对象所占的空间在堆上被分配,然后返回其引用;


2)二维数组

那么二维数组是如何存储的呢?事实上,在Java中只有一维数组,二维数组是一个存放了数组的数组,如下代码及示意图:

int[][] arr = new int[3][];arr[0] = new int[3];arr[1] = new int[5];arr[2] = new int[4];

对于多维数组来说,道理是一样的;

由此可见,数据量相同的情况下,开辟多维数组会产生更大的开销。

3)趣事:对于java二维数组建议 Int[][] arr=new Int[2][100] 而不要使用int[][] arr=new int[100][2],因为后者会产生更多的开销。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:

https://blog.csdn.net/weixin_40449300/article/details/83832947




粉丝福利:108本java从入门到大神精选电子书领取

???

?长按上方锋哥微信二维码 2 秒
备注「1234」即可获取资料以及
可以进入java1234官方微信群



感谢点赞支持下哈 


浏览 16
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报