我有一个存在内存问题的 Java 应用程序(在 OpenJDK 1.8.0_181 下运行)。特别是在一段时间后,所有物理内存似乎都被消耗了,并且应用程序无法分配更多内存。困难的部分是导致问题的不是堆内存。我正在努力了解如何进一步调查它,因为它不是堆内存。
以下是我遇到的内存崩溃类型:
Exception in thread "eXistThread-22" java.lang.OutOfMemoryError: unable to create new native thread
或者
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000773200000, 142606336, 0) failed; error='Cannot allocate memory' (errno=12)
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 142606336 bytes for committing reserved memory.
在所有这些情况下,服务器的物理内存都被完全消耗掉了。然后应用程序无法分配内存或扩展堆,因此它崩溃了。
首先是top
命令的结果:
KiB Mem : 7867456 total, 454608 free, 6696500 used, 716348 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 933392 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18 root 20 0 8827048 6.180g 28028 S 0.0 82.4 24:41.10 java
Java 应用程序使用以下内存设置运行:java -Xms512m -Xmx3072m
所以应用程序最多可以使用 3GB 的堆空间。但是您可以看到 java 进程(RES 内存)已经使用了超过 6GB 的 RAM。
在这一点上,很明显我需要查看非堆空间。有所谓的元空间,我认为它面临内存泄漏。我做了以下检查:
# jcmd 18 GC.heap_info
18:
PSYoungGen total 984576K, used 647154K [0x0000000780000000, 0x00000007c0000000, 0x00000007c0000000)
eden space 925696K, 63% used [0x0000000780000000,0x00000007a3e83970,0x00000007b8800000)
from space 58880K, 99% used [0x00000007b8800000,0x00000007bc179030,0x00000007bc180000)
to space 64000K, 0% used [0x00000007bc180000,0x00000007bc180000,0x00000007c0000000)
ParOldGen total 1671168K, used 1048981K [0x0000000700000000, 0x0000000766000000, 0x0000000780000000)
object space 1671168K, 62% used [0x0000000700000000,0x0000000740065488,0x0000000766000000)
Metaspace used 2235431K, capacity 2246406K, committed 2252780K, reserved 2981888K
class space used 317765K, capacity 320038K, committed 321024K, reserved 1048576K
在这里我看到使用的内存是 647154K + 1048981K 的堆和 2235431K 的元空间(非堆)。总量为 3931566K = 3840M = 3.75 GB。但是 Java 进程消耗的剩余内存是多少呢?很多:6.18 - 3.75 = 2.43 GB 的 RAM。
我还使用 jstat 检查了更多统计信息:
# jstat -gccapacity 18
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
174592.0 1048576.0 1048576.0 64000.0 58880.0 925696.0 349696.0 2097152.0 1671168.0 1671168.0 0.0 2981888.0 2252780.0 0.0 1048576.0 321024.0 289 13
在这里,我使用以下公式计算整个 Java 内存:
NGCMN + S0C + S1C + EC + OGCMN + MC + CCSC = 174592 + 64000 + 58880 + 925696 + 349696 + 2252780 + 321024 = 4146668K = 3.95 GB
它比 Java 进程消耗的 3.75 GB 略多,GC.heap_info
但仍远低于 Java 进程消耗的 6.18 GB。
所以我有点迷茫,我需要一个建议:
- 如何确定哪种类型的 Java 内存占用 2.43 GB?
- 如何限制该内存或确保及时清除它以避免 OutOfMemory 崩溃?
- 我是否应该设置额外的参数,例如 -XX:MaxMetaspaceSize -XX:MaxMetaspaceFreeRatio?如果是这样,这些的合理值是多少?