java - Java进程中出现OutOfMemoryException,但Used Heap大约是Used Size的一半

标签 java out-of-memory

我们正在运行一个具有消耗大量内存的缓存的进程。 但是该缓存中的对象数量在执行期间保持稳定,而内存使用量却在无限增长。

我们运行了 Java Flight Recorder 以尝试猜测发生了什么。

在那份报告中,我们可以看到 UsedHeap 大约是 UsedSize 的一半,对此我找不到任何解释。

JVM 退出并转储 OutOfMemory 报告,您可以在此处找到: https://frojasg1.com/stackOverflow/20210423.outOfMemory/hs_err_pid26210.log

这是整个 Java Flight Recorder 报告: https://frojasg1.com/stackOverflow/20210423.outOfMemory/test.7z

And a sample of that report

有人知道为什么会出现这种 outOfMemory 吗?

也许我必须改变问题......并问:为什么有将近 10 GB 的已用内存未在堆中使用?

最佳答案

日志文件是这样说的:

# Native memory allocation (mmap) failed to map 520093696 bytes 
for committing reserved memory.

所以发生的事情是 JVM 通过 mmap 系统调用从操作系统请求了大约 500MB 的内存块,但操作系统拒绝了。

当我查看更多日志文件时,很明显 G1GC 本身正在请求更多内存,看起来它在尝试扩展堆1 时正在这样做。

我能想到mmap 失败的几个可能原因:

  1. 操作系统可能没有交换空间来支持内存分配。

  2. 您的 JVM 可能已达到每个进程的内存限制。 (在 UNIX/Linux 上,这是作为 ulimit 实现的。)

  3. 如果您的 JVM 在 Docker(或类似)容器中运行,您可能已经超出了容器的内存限制。


这不是“正常”的 OOME。这实际上是 JVM 的内存需求与操作系统可用内存之间的不匹配。

  • 它可以在操作系统级别解决;即通过删除或增加限制,或添加更多交换空间(或可能更多 RAM)。

  • 也可以通过减少 JVM 的最大堆大小来解决这个问题。这将阻止 GC 尝试将堆扩展到不可持续的大小2。这样做也可能导致 GC 运行得更频繁,但这比应用程序因可避免的 OOME 过早死亡要好。


1- 在 G1GC 诊断方面具有更多经验的人可能能够从故障转储中辨别出更多信息,但对我来说这看起来像是正常的堆扩展行为。没有明显迹象表明正在创建一个“巨大”的对象。
2 - 计算出可持续大小实际上需要分析整个系统的内存使用情况,并查看可用的 RAM 和交换资源以及限制。那是系统管理问题,而不是编程问题。

关于java - Java进程中出现OutOfMemoryException,但Used Heap大约是Used Size的一半,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67231088/

相关文章:

java - 对象未写入文件触发 FileNotFoundException

java - 为什么我无法在 Solr 4.10.4 中创建核心 'blog'?

python - 如何在 python/OpenCV 中执行一系列非常大的图像平均?

android - 如何增加 Android 2.3 (Gingerbread) 上的堆大小?

java - Android: "Texting" ListView ?

java - 如何保护 chromedriver 使用的端口?

java - Struts2拦截器——如何读取action参数

java - 由于内存不足错误导致 cassandra 意外关闭

java - Finalizer 线程会导致内存不足吗?

out-of-memory - PermGen 的标志未按预期工作 : -XX:+CMSClassUnloadingEnabled and -XX:+CMSPermGenSweepingEnabled