Java 8 应用程序使用所有系统 RAM,然后因 SIGBUS 而崩溃。这里发生了什么?

Java 8 Application using all of System RAM and then crashing with a SIGBUS. Whats going on here?

我有一个 Java 8 应用程序,它通过网络接收消息并使用 Java NIO MappedByteBuffer 写入多个内存映射文件。我有一个 reader 可以同时从这些文件中按顺序读取消息,并使用 MappedByteBuffer 再次删除读取的文件。一切都很顺利,直到我写入和读取了大约 246 GB 的数据并且我的应用程序崩溃并出现以下

[thread 139611281577728 also had an error][thread 139611278419712 also had an error][thread 139611282630400 also had an error][thread 139611277367040 also had an error][thread 139611283683072 also had an error][thread 139611279472384 also had an error]





[thread 139611280525056 also had an error]
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGBUS (0x7) at pc=0x00007f02d10526de, pid=44460, tid=0x00007ef9c9088700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_101-b13) (build 1.8.0_101-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.101-b13 mixed mode linux-amd64 )
# Problematic frame:
# v  ~StubRoutines::jint_disjoint_arraycopy
#
# Core dump written. Default location: /home/user/core or core.44460
#
# An error report file with more information is saved as:
# /home/user/hs_err_pid44460.log

hs_err_pid44460.log 是空的,核心转储 core.44460 的大小约为 246 GB,里面装满了我要写的消息。

我 运行 最大堆大小为 32 GB。根据 JConsole,我 运行 超出 Free Physical Memory 然后崩溃。

为什么我 运行内存不足?我是不是忘记关闭某些文件句柄/没有正确关闭我的 MMapped 文件?

您必须查看进程的虚拟内存占用量或内存映射,以确定直接缓冲区是否可能是罪魁祸首。

如果它确实由于映射或直接缓冲区而崩溃,那么你要么泄漏它们(运行 通过内存分析器的堆转储可以识别那些)或者 GC 运行 太不频繁释放他们。

即使您的程序在使用 MappedByteBuffers 方面可能确实是正确的,但请注意,在高分配速度下,您可能会因不及时释放所述缓冲区而导致现象,这最终是 JVM 的责任,应该只发生在缓冲区的垃圾收集期间。简而言之,缓冲区的释放最终会成功,但何时它会发生应该很难预测。

但是,您可以使用 JVM 的清理器功能 (class sun.misc.Cleaner) 强制释放 ("cleaning") 分配给缓冲区的内存。请参考 this SO question 了解一些说明,但长话短说,您应该尽早对一次性缓冲区调用 Cleaner.clean(),以减少内存分配数字并有效支持您的用例。

您可能还会发现更积极的垃圾回收会有所帮助。

也可能想试试这个在 J7 中引入的选项: -XX:+使用G1GC -XX:ParallelGCThreads=4 这将允许 4 个线程并行进行 GC

有很多关于进一步调整垃圾收集器的好文章,我发现其中一篇很有用 (https://blogs.oracle.com/java-platform-group/entry/g1_from_garbage_collector_to)

希望对您有所帮助。