Java:OutOfMemoryError异常和freeMemory()函数

6

I have the following test program:

public static void main(String[] args)
{       
    HashMap<Integer, String> hm = new HashMap<Integer,String>(); 
    int i = 1;  
    while(true)
    {
        hm.put(i, "blah");
        i++;
        System.out.println("############"); 
        System.out.println("Max mem: " + Runtime.getRuntime().maxMemory()); 
        System.out.println("Total mem: " + Runtime.getRuntime().totalMemory()); 
        System.out.println("Free mem:" + Runtime.getRuntime().freeMemory());
    }
}

如果我运行这个程序,我会得到以下输出:
...

    ############
    Max mem: 8060928

    Total mem: 8060928

    Free mem:334400

    Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at java.util.HashMap.addEntry(Unknown Source)
        at java.util.HashMap.put(Unknown Source)
        at Test.main(Test.java:14)

为什么我会收到“OutOfMemoryError”异常,即使方法freeMemory()返回有更多的可用内存?是否有办法使用所有的freeMemory()?
4个回答

5
HashMap类会在其中的条目数量增加时重新调整大小。即使您显示还有300K+的剩余空间,这可能仍然不足以处理哈希桶的重新调整大小。
void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) {
        threshold = Integer.MAX_VALUE;
        return;
    }
    // ***possible big allocation here***
    Entry[] newTable = new Entry[newCapacity];
    transfer(newTable);
    table = newTable;
    threshold = (int)(newCapacity * loadFactor);
}

从一个更一般的意义上来说,在Java中,不建议对堆内存(以及整个进程大小)进行细粒度的期望。 有后台分配以及堆中尚未回收的对象占据了您可能没有预期到的空间。 此外,垃圾回收器在接近满堆时使用越来越多的CPU。 您需要运行大量的内存超出预期的最大分配大小。


4
  1. Runtime.freeMemory() javadoc 表示它返回的是"当前可用于未来分配对象的总内存近似值"

  2. 所有动态结构的工作方式都是分配内存块。当 HashMap 即将满时,它不会为一个对象再额外分配空间。它会分配一块某个大小的内存块。我不知道在JVM中它确切的工作方式,但它可能尝试分配比它当前使用的内存量多两倍的内存。


1

当HashMap需要扩展其内部存储时,您将会遇到内存不足异常。这将需要比可用的内存更大的要求。


但是当我使用LinkedList而不是HashMap时,我遇到了同样的问题。在我的实际情况中,我想读取1000万条数据记录,每个记录都是一个HashMap,并将所有HashMap存储在LinkedList中。jvisualvm告诉我,我的程序最多可以使用80GB,但它只使用了65GB。在这一点上,我遇到了异常... - user1053813
@user1053813,你可以这样做,但是你需要给你的应用程序大量的内存,我猜可能需要很多GB。现在内存很便宜,所以这不必成为问题。如果你想要数据存储更高效,我建议使用对象代替HashMap。(可以缩小2-3倍)也有可能存在某种与操作系统相关的内存屏障,防止你在一个应用程序中使用超过64GB的内存。你可以考虑使用直接内存来存储数据,这可以减少一半的对象大小,并且使用很少的堆内存,但需要更多的工作。 ;) - Peter Lawrey

0

看起来可用的内存不足以运行JVM。


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