非堆空间中的Java内存泄漏

时间:2021-02-09 18:47:48

标签: java jvm heap-memory metaspace

我有一个存在内存问题的 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的内存。

此时很明显我需要查看非堆空间。有所谓的 Metaspace,我认为它面临内存泄漏。我已经完成了以下检查:

# 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 内存。

我还使用 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

根据 GC.heap_info 计算出的空间略大于 3.75 GB,但仍远低于 Java 进程消耗的 6.18 GB。

所以我有点迷茫,我需要一个建议:

  • 如何确定哪种类型的 Java 内存占用了 2.43 GB?
  • 如何限制内存或确保及时清除内存以避免 OutOfMemory 崩溃?
  • 我应该设置像 -XX:MaxMetaspaceSize -XX:MaxMetaspaceFreeRatio 这样的额外参数吗?如果是这样,这些的合理值是多少?

0 个答案:

没有答案