Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么这段代码需要这么多内存_Java_Out Of Memory - Fatal编程技术网

Java 为什么这段代码需要这么多内存

Java 为什么这段代码需要这么多内存,java,out-of-memory,Java,Out Of Memory,我目前正试图解决一个在线编程竞赛的问题。在这次比赛中,该程序的限制是64兆字节 我用Java编写了一个程序,在类声明中有一部分字段,其工作方式如下: private int[] sizes = new int[1024]; // 4096 bytes private boolean[][] compat = new boolean[1024][1024]; // 128 kb private boolean[][] compat2 = new boolean[1024][1024]; // 12

我目前正试图解决一个在线编程竞赛的问题。在这次比赛中,该程序的限制是64兆字节

我用Java编写了一个程序,在类声明中有一部分字段,其工作方式如下:

private int[] sizes = new int[1024]; // 4096 bytes
private boolean[][] compat = new boolean[1024][1024]; // 128 kb
private boolean[][] compat2 = new boolean[1024][1024]; // 128 kb

private long[][][] dp = new long[29000][51][2]; // About 3*8 = 24 megabytes
private int [][] masks  = new int[29000][2]; // About 240 kb
private int avail = 0; 
private int avail2 = 0;
private int[] positions = new int[500000]; // About 2 megabytes
private int[][] ranges = new int[29000][2]; // About 240 kb
private int[][] maskToPos = new int[1024][1024]; // About 4 megabytes
private int[][][] init = new int[29000][51][2]; // About 3*4 = 12 megabytes
private long[][][] dp = new long[51][2][29000];
现在,该类中只有一个主过程和一些循环,没有声明任何附加数组(只有一些变量可以在循环中迭代)。然而,当我尝试在本地机器上使用键-Xmx64m运行此代码时,我遇到了一个OutOfMemoryError。它只使用密钥Xmx128m执行

我还尝试在在线服务器上启动,它给出了相同的错误,还提供了额外的信息,我的程序使用了大约148460KB


但为什么这么多?据我从上面的片段计算,它应该只使用大约40兆字节。注释中的这种计算有问题吗?

问题是Java中的多维数组不是真正的多维数组;如果是,那么Java将支持[x,y]符号。但事实并非如此。因为Java中的多维数组是作为数组的数组实现的。因此,
newboolean[1024][1024]
实际上是1024个数组对象,每个对象包含1024个布尔值。(每个1KB)


我不记得哪个维度是主维度,哪个维度是次维度,但根据程序内存不足的事实判断,第一个维度可能是主维度。因此,
new long[29000][51][2]
是29000*51=1479000个数组对象,每个对象包含2个长值。所以,有这么多对象,考虑到每个对象的开销,忘掉它吧

这两个是最大的杀手:

private long[][][] dp = new long[29000][51][2]; // About 3*8 = 24 megabytes
private int[][][] init = new int[29000][51][2]; // About 3*4 = 12 megabytes
看看第二个,例如。。。这不是12兆字节。您有29000个
int[]]
对象,每个对象都包含对51个
int[]
对象的引用,每个对象都包含2个整数

假设数组本身有32位参考大小和16字节的开销(长度+公共对象开销),这意味着
int[][]
对象的大小都是51*4+16=220字节,然后
int[]
对象的大小都是24字节。但是你有29000*51个24字节的对象-这本身就是35MB。。。然后是29000个
int[][]
对象,这是另一个6MB。。。(然后是顶级阵列本身,但仅为120K左右。)

基本上,您需要记住Java没有多维数组:它有数组的数组,每个数组都是一个对象,有单独的开销。我建议您可以使用:

private int[] init = new int[29000 * 51 * 2];
取而代之的是,自己制定适当的补偿。(dp也是如此,它的
long
值比
int
值更糟糕,这使得29000*51数组中的每个数组至少占用32字节,而不是24字节。)

即使只是颠倒处理尺寸的顺序也会有所帮助:

现在,对于这些变量中的每一个,都有一个顶级数组,两个数组,和102个
long
int
数组。这相当于大大减少了开销


你的其他计算也不正确,但我认为这两个数组的数组是最差的。

如上所述,
long[29000][51][2]
需要超过24兆字节。您可以尝试通过将最大维度移动到数组末尾来减少内存量,如下所示:

private int[] sizes = new int[1024]; // 4096 bytes
private boolean[][] compat = new boolean[1024][1024]; // 128 kb
private boolean[][] compat2 = new boolean[1024][1024]; // 128 kb

private long[][][] dp = new long[29000][51][2]; // About 3*8 = 24 megabytes
private int [][] masks  = new int[29000][2]; // About 240 kb
private int avail = 0; 
private int avail2 = 0;
private int[] positions = new int[500000]; // About 2 megabytes
private int[][] ranges = new int[29000][2]; // About 240 kb
private int[][] maskToPos = new int[1024][1024]; // About 4 megabytes
private int[][][] init = new int[29000][51][2]; // About 3*4 = 12 megabytes
private long[][][] dp = new long[51][2][29000];

这可能足以让您的程序在编程竞赛中勉强通过。

一个小建议:我会尝试让您的所有声明都成为“最终声明”。大数组会导致内存分配问题,因为不仅必须找到空间,还必须找到连续空间。Java可以移动东西来腾出空间,但如果移动时间过长,即使理论上空间可用,它也会抛出内存不足异常。你似乎是在回避这个问题,把你所有的内存都提前抓起,一直保存到程序结束。使用“final”会让JVM知道您对此很认真,也许会让它以一种帮助您的方式分配空间


这可能对JVM没有帮助。我发现Java在最近几年变得非常聪明,它可能不需要你告诉它什么是最终的,什么不是。然而,人们确实需要被告知。使用“final”可以防止您和其他更改代码的人意外地重新分配空间,比如使用
positions=newint[500010]这样的语句在你的代码中的其他地方,并压倒了JVM/垃圾收集器。

不要忘记JVM本身也需要内存。为什么它需要这么多内存来存储所有这些变量?我要在这里冒一个险,说这对于任何在线网站来说看起来都太过了(主要是因为我在很多网站上解决了大约300-500个问题,从来没有写过这样的东西)此外,Java在存储元数据方面也有开销,您没有考虑到这一点。重新考虑您的解决方案可能是您最好的选择,因为您永远不需要那么多内存。如果需要,我将重新考虑我的解决方案。那么,元数据中存储的哪些内容会导致这样的开销呢?
new boolean[1024][1024]
是1 MB,不是128 KB。我认为布尔值是1位,不是1字节?很少(如果有的话)JVM实现优化了布尔的内存使用。他们喜欢用像所有其他原语一样的字节地址来处理布尔,并将内存压缩留给像BitSet这样的对象。我想我的黑暗C++过去对我起了一个坏的把戏,我曾经习惯于int [29000 ] [51 ] [2 ]和int [29000×51 *2 ]一样多的内存。谢谢你的回答。这也是一个很好的理由来考虑使用<代码> int [29000 ] [ 51×2 ] < /代码>或<代码> int [ 29000×51*2 ] < /代码>。