Java集合:当“size”超过“int”时会发生什么?

时间:2010-08-23 13:12:00

标签: java memory collections integer overflow

我知道Java集合非常需要内存,并且自己做了一个测试,证明4GB几乎不足以将IntegerHashSet存储到Collection.size()中。

但是如果我有“足够的”记忆呢? Collection.size()会发生什么?

编辑已解决:Integer.MAX超出整数范围时返回reference + cached_hashcode + boxed_integer_object + real_int_value
新问题:如何确定集合元素的“真实”数量呢?

注意1:抱歉,这可能是一个让我谷歌给你的问题,但我真的没找到任何东西;)

注2:据我所知,一组的每个整数条目是: VisualVM,对吧?

注3:有趣的是,即使使用JDK7和“压缩指针”,当JVM使用2GB的实内存时,它在import java.util.*; import java.lang.management.*; public final class _BoxedValuesInSetMemoryConsumption { private final static int MILLION = 1000 * 1000; public static void main(String... args) { Set<Integer> set = new HashSet<Integer>(); for (int i = 1;; ++i) { if ((i % MILLION) == 0) { int milsOfEntries = (i / MILLION); long mbytes = ManagementFactory.getMemoryMXBean(). getHeapMemoryUsage().getUsed() / MILLION; int ratio = (int) mbytes / milsOfEntries; System.out.println(milsOfEntries + " mil, " + mbytes + " MB used, " + " ratio of bytes per entry: " + ratio); } set.add(i); } } } 中仅显示1.5GB的已分配内存。

对于那些关心的人:

测试来源:

-XX:+UseCompressedOops -Xmx2048m

执行参数:

在OpenSuse 11.3 x64下使用x64版本的JDK7 build 105进行测试。

1 mil, 56 MB used,  ratio of bytes per entry: 56
2 mil, 113 MB used,  ratio of bytes per entry: 56
3 mil, 161 MB used,  ratio of bytes per entry: 53
4 mil, 225 MB used,  ratio of bytes per entry: 56
5 mil, 274 MB used,  ratio of bytes per entry: 54
6 mil, 322 MB used,  ratio of bytes per entry: 53
7 mil, 403 MB used,  ratio of bytes per entry: 57
8 mil, 452 MB used,  ratio of bytes per entry: 56
9 mil, 499 MB used,  ratio of bytes per entry: 55
10 mil, 548 MB used,  ratio of bytes per entry: 54
11 mil, 596 MB used,  ratio of bytes per entry: 54
12 mil, 644 MB used,  ratio of bytes per entry: 53
13 mil, 827 MB used,  ratio of bytes per entry: 63
14 mil, 874 MB used,  ratio of bytes per entry: 62
15 mil, 855 MB used,  ratio of bytes per entry: 57
16 mil, 902 MB used,  ratio of bytes per entry: 56
17 mil, 951 MB used,  ratio of bytes per entry: 55
18 mil, 999 MB used,  ratio of bytes per entry: 55
19 mil, 1047 MB used,  ratio of bytes per entry: 55
20 mil, 1096 MB used,  ratio of bytes per entry: 54
21 mil, 1143 MB used,  ratio of bytes per entry: 54
22 mil, 1191 MB used,  ratio of bytes per entry: 54
23 mil, 1239 MB used,  ratio of bytes per entry: 53
24 mil, 1288 MB used,  ratio of bytes per entry: 53
25 mil, 1337 MB used,  ratio of bytes per entry: 53
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

输出结果:

{{1}}

最后,使用了大约2 GiB实内存,而不是显示1.3 GiB,因此每个条目的消耗甚至更大超过53个字节。

4 个答案:

答案 0 :(得分:14)

  

我知道Java集合非常多   记忆力饥渴,自己做了一个测试,   证明4GB几乎不足以   将数百万Integers存储到一个   HashSet

Java堆!=系统内存。 Java的默认堆大小仅为128MB。请注意,这也与JVM使用的内存不同。

关于你的问题:来自文档,

public int size()

  

返回此元素的数量   采集。如果这个集合   包含的内容超过Integer.MAX_VALUE   元素,返回Integer.MAX_VALUE

答案 1 :(得分:6)

您的问题似乎与标题完全不同。

您已在标题中回答了问题(已返回Integer.MAX_VALUE)。并且没有:你无法找到普通API的“真实”大小,以便迭代收集和计数(当然使用long)。

如果您想存储Set int个值,并且您知道范围的值可能会变得非常大,那么BitSet可能实际上是一个更好的实现:

import java.util.*;
import java.lang.management.*;

public final class IntegersInBitSetMemoryConsumption {
  private final static int MILLION = 1000 * 1000;

  public static void main(String... args) {
    BitSet set = new BitSet(Integer.MAX_VALUE);

    for (int i = 1;; ++i) {
      if ((i % MILLION) == 0) {
        int milsOfEntries = (i / MILLION);
        long mbytes = ManagementFactory.getMemoryMXBean().
            getHeapMemoryUsage().getUsed() / MILLION;
        double ratio = mbytes / milsOfEntries;
        System.out.println(milsOfEntries + " mil, " + mbytes + " MiB used, "
            + " ratio of bytes per entry: " + ratio);
      }

      set.set(i);
    }
  }
}

这将产生一个恒定大小的数据结构,它可以保存范围内的所有值,而不会改变大小并占用相对少量的内存(每个可能值加1位加上一些开销)。

然而,这种方法有两个缺点:

  • 它不支持负int
  • 它不提供Set API

通过编写使用两个BitSet对象(可能是懒惰分配)的包装器分别保存正值和负值范围并实现Set接口的适配器方法,可以轻松解决这两个问题。 / p>

答案 2 :(得分:3)

来自源代码:

 /**
 * Returns the number of elements in this collection.  If this collection
 * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
 * <tt>Integer.MAX_VALUE</tt>.
 * 
 * @return the number of elements in this collection
 */
int size();

答案 3 :(得分:0)

任何真正的处理器架构的通用答案都是你做不到的。原因很简单:没有比可寻址存储器更多的分配对象(至少1个字大小)。

当然,考虑到JVM的虚拟特性,可能会出现这种情况。 int将始终为32位签名,您可以在64位机器上实现并运行JVM,其中可以处理超过2GB的内存。

在这种情况下,文档告诉我们将返回Integer.MAX_INT ...这是一个大问题,因为任何使用依赖于i < col.size()的整数变量停止的循环将永远运行(虽然我认为循环2**31-1次的任何事情都需要很长时间才能让你想要杀死这个过程。)