填充哈希映射时超过GC开销限制



当我的java程序启动时,它会用数千个对象填充一个hashmap。键是一个字符串,值是一组对象。

该程序正在全速运行,当它达到大约10000个键时,会抛出内存不足异常:GC开销限制超出。

我读到,可能是底层数组必须不断调整大小。但我希望能够在不增加堆大小的情况下解决这个问题。

谢谢!

为了确定堆需求,您需要对数据的大小以及HashMap的每个元素开销进行建模。

为了简单起见,我假设您运行的是一个使用压缩OOPS(OOP=普通对象指针)的64位JVM。这为每个对象提供了一个12字节的标头和一个4字节的对象引用。我将进一步假设您使用的是默认负载因子为0.75的HashMap

对于10000个元素,表大小至少为10000/0.75=11333。然而,表的大小总是二的幂,所以它可能有16384长。这提供了65536字节--64KB。

存储在HashMap中的每个元素还需要创建一个内部Node对象,该对象有四个4字节的字段(hash、key、value、next)加上对象头的12个字节,每个Node对象有28个字节。有10000个元素,即280KB。

因此,HashMap表大小加上内部Node对象需要344KB的开销来存储10000个键值对。这不会导致你的内存不足。更改HashMap的初始容量将减少调整大小的复制开销,但与数百MB或几GB的典型堆大小相比,它所占用的临时额外内存量可以忽略不计。

如果您的堆大小是1GB,并且10000个映射条目的内存不足,那么每个键值对大约需要100KB。除非大幅增加堆大小,或者减少每个键值对的大小,或者两者的组合,否则您将无法加载几百万个键。

您正在获得内存不足,因为每次满足特定阈值时,内部数组都会加倍。所以你需要确保它不会发生。如果您知道需要存储在映射中的对象数量,只需使用构造函数HashMap(int initialCapacity, float loadFactor),并在第一个参数中提供预期的大小。如果你不知道对象的数量,你仍然可以尝试设置initialCapacity,以获得一些近似值和/或使用loadFactor参数(默认值为0.75)-值越大,调整大小的阈值就越大。

对于性能和内存相关的问题,应该使用评测器和内存分析器工具。我要做的一个技巧是用标志配置JVMJAVA_OPTS=-XX:HeapDumpOnOutOfMemoryError然后,当您遇到这个异常和JVM崩溃时,您可以使用这个转储并使用MAT内存分析器工具来分析对象分布和根路径。此外,人们更喜欢使用与Java及其飞行记录器一起分发的Java任务控制JMC。请注意,自Java 7更新40 以来,飞行记录仪一直可用

相关内容

最新更新