本地内存分配(mmap)失败,无法映射

24

我开始遇到本地内存分配问题。我猜想可能与 -Xmx 和 -Xms 设置有关。设置这些值的推荐方法是什么?

目前我有:-Xmx13G -Xms6G

我读到建议设置相同的值,但没有任何解释为什么。

我得到的错误信息是:

    # There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 746061824 bytes for committing reserved memory.
# Possible reasons:
#   The system is out of physical RAM or swap space
#   In 32 bit mode, the process size limit was hit
#   Decrease number of Java threads
#   Decrease Java thread stack sizes (-Xss)
#   Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#
#  Out of Memory Error (os_linux.cpp:2627), pid=13528, tid=0x00007f2b0b5f5700
#
# 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 compressed oops)
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#

/proc/meminfo:
MemTotal:       16433112 kB
MemFree:          166336 kB
Buffers:          114324 kB
Cached:           398396 kB
SwapCached:            0 kB
Active:         15151496 kB
Inactive:         254348 kB
Active(anon):   14893020 kB
Inactive(anon):      604 kB
Active(file):     258476 kB
Inactive(file):   253744 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                12 kB
Writeback:             0 kB
AnonPages:      14892976 kB
Mapped:            24024 kB
Shmem:               696 kB
Slab:             349384 kB
SReclaimable:     187700 kB
SUnreclaim:       161684 kB
KernelStack:       43520 kB
PageTables:       276768 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     8216556 kB
Committed_AS:   33089080 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       31404 kB
VmallocChunk:   34359652884 kB
HardwareCorrupted:     0 kB
AnonHugePages:  13486080 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       28672 kB
DirectMap2M:    16879616 kB

Memory: 4k page, physical 16433112k(166336k free), swap 0k(0k free)

vm_info: Java HotSpot(TM) 64-Bit Server VM (25.101-b13) for linux-amd64 JRE (1.8.0_101-b13), built on Jun 22 2016 02:59:44 by "java_re" with gcc 4.3.0 20080428 (Red Hat 4.3.0-8)

你需要使用这么多内存有什么特别的原因吗?也许现在是考虑将当前未使用的部分换到磁盘上的时候了。 - Tibrogargan
嗨,谢谢。我们有一个内存缓存(Spring),我们正在限制对象的数量并清除直到达到某个限制,但内存仍然在增长。我想我必须通过转储来分析发生了什么。谢谢。 - juancevi
2
如果你将 -Xmx-Xms 设置为相同的值,那么你就可以从一开始就知道 Java 是否有足够的内存来运行,而且你也不会浪费时间去扩展分配给 Java 进程的内存。 - AlikElzin-kilaka
5个回答

16
你的系统明显需要比物理内存更多的内存。你总共有16GB,但使用率已经达到了90%,而且没有交换空间,因此根本无法获得-Xms6G,更不用说更大的-Xmx13G了。
你需要找出使用内存的其他进程,例如使用“top”命令并按常驻内存排序(大写字母O,然后q),并停止足够多的进程以释放至少6GB的内存,然后再运行JVM。
或者将物理内存加倍至32GB,或添加16GB的交换空间(但如果系统负载过重会导致磁盘频繁读写)。

你好,感谢您的回复。目前只有一个应用程序正在使用该服务器。我们使用Spring Cache实现了一个内存缓存,这可能是问题的来源。我们将此缓存限制为存储一定数量的对象,但问题仍然存在。我猜测存在某种内存泄漏,因为即使在运行时清除该缓存的空间,内存仍然保持高位。谢谢。 - juancevi
你看了我建议的使用 top 查看内存使用情况吗?Spring Cache 是一个单独的进程吗?你能限制 Spring Cache 的内存使用量到特定大小(以字节为单位,而不是对象数量)吗? - Jim Garrison
SpringCache是同一进程,我们正在使用的缓存实现(guava)没有提供指定大小限制的方法。关于top,我已经使用了下面的输出:<br/> PID USER PR NI VIRT RES SHR S %CPU %MEM 5491 xxx 20 0 13.969g 0.010t 14992 S 0.3 68.4 - juancevi

13

这种错误不仅可能因为堆空间总量用尽而发生,还可能因为内存映射区域的数量用尽而发生。

在Linux中,每个进程的最大映射区域数量由vm.max_map_count sysctl参数控制(默认值为65536)。因此,例如我会尝试将其加倍并检查结果:

sysctl -w vm.max_map_count=131072

当堆转储(heap dump)中的 "Dynamic Libraries" 部分列出大量打开的 mmap 区域时,这表明您遇到了这个问题的指示器。

就是这样。非常感谢您,先生。您的答案解决了我的问题。无论我设置-Xms -Xmx -Xss选项的值为多少,我都只能得到不超过32600个线程,它始终在32500到32600个线程之间。将其设置为131072后,第一次就达到了约40000个线程。 - falero80s
这解决了我与AOSP构建编译和dex2oat bootcalss相关的问题。 - Bhanu Surendra

5

可能的解决方案:

  • 减少系统上的内存负载
  • 增加物理内存或交换空间
  • 检查交换备份存储是否已满
  • 在64位操作系统上使用64位Java
  • 减少Java堆大小 (-Xmx/-Xms)
  • 减少Java线程数量
  • 减小Java线程栈大小 (-Xss)
  • 使用-XX:ReservedCodeCacheSize=设置更大的代码缓存

5

Jim Garrison提供了一个非常好的答案,解释了为什么op会遇到这个问题。

我想回答op的一个次要问题:

我读到推荐设置相同的值,但没有任何解释为什么。

基本上,JVM将在启动JVM时分配放置在-Xms中的任何内容,然后根据需要增长到-Xmx,一旦达到该值,它将进行垃圾回收(刷新不再使用的内容)。

处理大量对象(此处为7Gb的对象)的GC并不是个好主意,因为这将花费时间和大量资源。将它们设置为相同的值是可以的,因为GC与之共同进行。 GC具有“停止世界”操作,其中在收集垃圾时不能运行其他任何内容。现在想象清理7Gb的垃圾, 这将需要相当长的时间并导致长时间的暂停。

你真的应该阅读https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/introduction.html


1
我认为这个答案中存在一个隐藏的假设,即当GC(垃圾收集器)到达“-Xmx”时才会运行,导致大量对象被清理。我不确定这是否正确。我认为GC并不等待“-Xmx”,而是在此之前就已经运行了。 - AlikElzin-kilaka
你有证据支持你的说法吗?据我所知,当达到Xmx时会执行完整的GC。这就是为什么Oracle建议将XmsXmx设置为相同值的原因。 - thecarpy

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接