2011-11-18 73 views
5

我有以下的测试程序:的Java:OutOfMemoryError异常异常和freeMemory()

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()); 
    } 
} 

如果我运行这个程序,我得到follwing输出:

... 

    ############ 
    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()?

回答

0

似乎可用内存量不足以运行JVM。

1

在HashMap需要扩展它的内部存储的时候,你会得到内存不足的异常。对此的要求将大于可用的感觉记忆。

+0

但是,当我使用LinkedList而不是HashMap时,我遇到了同样的问题。在我的真实环境中,我想读取10百万条数据记录,每个数据记录都是一个HashMap,并且我将所有HashMap存储在一个LinkedList中。 jvisualvm告诉我,我的程序可以使用max。 80GB,但它只使用65GB。在这一点上,我得到的例外...... – user1053813

+0

@ user1053813,你可以这样做,但是你需要给你的应用程序提供很多内存,我怀疑有很多GB。内存现在很便宜,所以不一定是个问题。如果你想要更有效地存储数据,我会使用对象而不是HashMap。 (可能小2-3倍)有可能存在某种与操作系统有关的内存障碍,从而阻止您在一个应用程序中使用超过64 GB的内存。您可能会考虑使用直接内存来存储数据,这可能是对象大小的一半,并且使用很少的堆,但是工作量更大。 ;) –

4
  1. Runtime.freeMemory() javadoc说,这是回报"an approximation to the total amount of memory currently available for future allocated objects"

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

4

HashMap课程随着其中条目的数量增加而调整大小。即使你显示300 + K空闲,这可能不足以处理哈希桶的大小调整。

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。您希望在预计的最大分配大小之上运行大量内存。