int[]和Integer[]数组的内存占用量

3

我尝试创建一个整数数组(我也尝试使用自己的对象,但使用int时出现了相同的情况),大小为3000万。我一直收到"OutOfMemoryError:Java堆空间"的错误提示。

Integer [] index = new Integer[30000000];
for (int i = 0 ; i < 30000000 ; i++){
    index[i] = i;
}

我检查了总堆空间,使用 "Runtime.getRuntime().totalMemory()" 和 "maxMemory()",发现我开始时有64 MB的空间,最大可达到900+ MB,在运行过程中,堆空间达到900+ MB并崩溃。

现在我知道一个整数占4个字节,所以即使我将30*4*1000000相乘,我也只能得到大约150-100兆字节。

如果我尝试使用原始类型,比如int,它可以工作。

我该怎么解决这个问题?


使用更多堆内存运行程序。 - Hovercraft Full Of Eels
5个回答

8

我相信他使用Integer只是为了演示目的。 我相信实际代码使用非Integer自定义类对象的数组。 - Hovercraft Full Of Eels
你是正确的,我使用自己的对象。但我不明白为什么它会占用这么多空间。 - Ben2307
是的,引用大小开销是让你困扰的问题。如果你正在使用Java 64位,你也可以尝试-XX:+UseCompressedOops来帮助减少引用的大小。 - Andrew White
CompressedOoops会将Integer[]数组的大小减半,但Integer对象的大小保持不变。 - Stephen C

4
假设我们在讨论一个基于32位OpenJDK的JVM。
- 每个Integer对象有1个int字段 - 占用4个字节。 - 每个Integer对象有2个标头字 - 占用8个字节。 - 分配的粒度是(我相信)2个字 - 4个字节的填充。 - Integer[]每个数组元素/位置都有1个引用 - 4个字节。
因此,每个数组元素总共有20个字节。20 x 30 x 1,000,000 = 600,000,000 Mbytes。现在加上代收集器将分配至少3个不同大小的对象空间,这可能会轻松增加到900多Mbyte。
如何修复它?
- 使用 int[] 代替 Integer。 - 如果 Integer 的值大多表示范围为-128到+127的数字,则使用 Integer.valueOf(int) 分配它们。JLS保证以这种方式创建的 Integer 对象将被共享。(注意,当通过自动装箱创建 Integer 时,JLS规定使用 valueOf。因此,在您的示例中已经应用了此“修复”)。 - 如果您的 Integer 值主要来自更大但仍然很小的域,请考虑为共享 Integer 对象实现自己的缓存。
我的问题是关于 Integer 作为示例,在我的程序中,我使用自己的对象,它只包含一个字节数组(最大大小为4)。当我创建它时,它占用的内存比4个字节多得多。
是的,确实如此。
假设您的类定义如下:
public class MyInt {
    private byte[] bytes = new byte[4];
}

每个将占用:
- MyInt头部单词 - 8字节 - MyInt.bytes字段 - 4字节 - 填充 - 4字节 - 字节数组的头部单词 - 12字节 - 数组内容 - 4字节
现在加上MyInt引用所占的空间:
- 每个MyInt的引用 - 4字节
总计 - MyInt[]的每个元素占用36字节。
与Integer[]的20字节或int[]的4字节相比,这是一个很大的差距。
如何修复这种情况?
嗯,一个包含4字节的数组包含32位的数据。 这可以编码为int。因此修复方法与之前相同。 使用int[]而不是MyInt [],或者(可能)调整上面讨论的其他想法之一。
另外,可以增加堆大小,或使用数据库或类似的东西,使数据不需要保存在RAM中。

我的问题是关于整数的例子,在我的程序中,我使用自己的对象,它只包含一个字节数组(最大大小为4)。 当我创建它时,它占用的内存远远超过4个字节。 - Ben2307

0

这不是你要找的,但在这个简单的例子中,最优解是使用函数而不是数组。

static int index(int num) {
    return num;
}

如果您有更现实的例子,可能会有其他优化方法可用。


0

Integer 是一个占用超过 4 字节的对象。具体占用多少取决于实现方式。你真的需要 Integer 吗?唯一的好处是它可以是 null。也许你可以使用“哨兵值”代替,比如 -1 或者 Integer.MIN_VALUE


0
也许你应该使用数据库而不是一个巨大的数组,但如果你必须使用一个巨大的对象数组,你尝试过在运行Java应用程序启动器时使用-Xms命令行参数来增加Java内存大小吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接