我正在尝试将大约50,000个对象(因此50,000个键)插入java.util.HashMap<java.awt.Point, Segment>。但是,我一直得到一个OutOfMemory异常。 (Segment是我自己的班级 - 非常轻量级 - 一个String字段,以及3个int字段)。

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.HashMap.resize(HashMap.java:508)
    at java.util.HashMap.addEntry(HashMap.java:799)
    at java.util.HashMap.put(HashMap.java:431)
    at bus.tools.UpdateMap.putSegment(UpdateMap.java:168)

这看起来非常荒谬,因为我发现机器上有足够的内存 - 无论是在免费RAM还是用于虚拟内存的高清空间。

Java是否有可能运行时有一些严格的内存要求?我可以增加这些吗?

HashMap有一些奇怪的限制吗?我是否必须实施自己的?还有其他值得关注的课程吗?

(我在具有2GB RAM的Intel机器上运行OS X 10.5下的Java 5。)

有帮助吗?

解决方案

您可以通过将-Xmx128m(其中128是兆字节数)传递给java来增加最大堆大小。我不记得默认的尺寸,但它让我觉得它很小。

您可以使用运行时课程。

// Get current size of heap in bytes
long heapSize = Runtime.getRuntime().totalMemory();

// Get maximum size of heap in bytes. The heap cannot grow beyond this size.
// Any attempt will result in an OutOfMemoryException.
long heapMaxSize = Runtime.getRuntime().maxMemory();

// Get amount of free memory within the heap in bytes. This size will increase
// after garbage collection and decrease as new objects are created.
long heapFreeSize = Runtime.getRuntime().freeMemory();

Java Developers Almanac 中的示例)

有关Java HotSpot VM的常见问题解答中也部分解决了这个问题。 ,以及 Java 6 GC Tuning页面

其他提示

有些人建议更改HashMap的参数以收紧内存需求。我建议衡量而不是猜测;它可能是造成OOME的其他因素。特别是,我建议使用 NetBeans Profiler VisualVM (Java 6附带,但我发现你已经厌倦了Java 5)。

如果事先知道对象的数量,另一件事就是使用HashMap(int capacity,double loadfactor)构造函数而不是默认的no-arg,它使用默认值(16,0.75)。如果HashMap中的元素数量超过(capacity * loadfactor),那么HashMap中的基础数组将调整为下一个2的幂,表格将被重新处理。此阵列还需要一个连续的内存区域,例如,如果从32768到65536大小的阵列加倍,则需要256kB的内存块。为了避免额外的分配和重新处罚,只需从一开始就使用更大的哈希表。它还会降低你不会有足够大的内存连续区域来适应地图的可能性。

这些实现通常由数组支持。数组是固定大小的内存块。 hashmap实现首先将数据存储在给定容量的其中一个数组中,比如100个对象。

如果它填满了数组并且你不断添加对象,那么地图需要秘密增加它的数组大小。由于数组是固定的,它通过在内存中创建一个全新的数组以及稍大的当前数组来实现。这被称为增长阵列。然后将旧数组中的所有项目复制到新数组中,并取消引用旧数组,希望它将被垃圾收集并在某些时候释放内存。

通常,通过将项目复制到更大的数组来增加地图容量的代码是导致此类问题的原因。有<!>“dumb <!>”;实现和智能的,使用增长或加载因子,根据旧数组的大小确定新数组的大小。有些实现隐藏了这些参数,有些则没有,所以你不能总是设置它们。问题是当你无法设置它时,它会选择一些默认的加载因子,比如2.所以新数组的大小是旧数组的两倍。现在你所说的50k地图有一个100k的支持数组。

看看你是否可以将负载系数降低到0.25或更低。这会导致更多的哈希映射冲突,从而影响性能,但是你遇到了内存瓶颈,需要这样做。

使用此构造函数:

http:// java.sun.com/javase/6/docs/api/java/util/HashMap.html#HashMap(int ,float))

启动java时,您可能需要设置标志-Xmx512m或更大的数字。我认为64mb是默认值。

编辑添加: 在你弄清楚你的对象实际使用的内存有多少内存后,你可能想要查看弱引用或软引用,以确保你没有意外地从垃圾收集器中拿走你的一些内存人质更长时间使用它们。

在这些答案中暗示Java具有固定的内存大小,并且不会超出配置的最大堆大小。这与C不同,它只受到运行它的机器的约束。

默认情况下Java堆空间有限,但这听起来仍然极端(虽然你的50000段有多大?)

我怀疑你还有其他问题,例如集合中的数组变得太大,因为所有内容都被分配到相同的“插槽”中。 (当然也会影响表现)。但是,如果您的积分均匀分布,那似乎不太可能。

我想知道为什么你使用的是HashMap而不是TreeMap?尽管点是二维的,但您可以使用比较函数对它们进行子类化,然后执行log(n)查找。

随机思考:与HashMap关联的哈希桶并不是特别节省内存。您可能希望尝试使用TreeMap作为替代方案,看看它是否仍能提供足够的性能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top