【问题标题】:Growing Resident Size Set in JVMJVM 中不断增长的驻留大小集
【发布时间】:2020-12-21 06:30:30
【问题描述】:

我有一个 JAVA 进程在 64 位 LINUX 上运行,版本为“CentOS Linux release 7.3.1611”,内存为 7.6GB。

以下是一些使用的 JVM 标志,

  1. -Xmx3500m
  2. -Xms3500m
  3. -XX:MaxMetaspaceSize=400m
  4. -XX:CompressedClassSpaceSize=35m

注意:默认线程栈大小(1MB)和代码缓存(240MB),JDK版本为1.8.0_252。

在运行 TOP 命令时,它观察到我的 6.3 GB RAM 由 java 持有 过程。

PR   NI    VIRT     RES    SHR S  %CPU %MEM   TIME+   COMMAND   
20   0  28.859g  6.341g  22544 S 215.2 83.1   4383:23 java    

我尝试使用 JCMD、JMAP 和 JSTAT 命令分析 JVM 的本机内存。

JMAP -heap 命令的输出:

Debugger attached successfully.
Server compiler detected.
JVM version is 25.252-b14

using thread-local object allocation.
Garbage-First (G1) GC with 33 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 40
   MaxHeapFreeRatio         = 70
   MaxHeapSize              = 3670016000 (3500.0MB)
   NewSize                  = 1363144 (1.2999954223632812MB)
   MaxNewSize               = 2202009600 (2100.0MB)
   OldSize                  = 5452592 (5.1999969482421875MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 36700160 (35.0MB)
   MaxMetaspaceSize         = 419430400 (400.0MB)
   G1HeapRegionSize         = 1048576 (1.0MB)

Heap Usage:
G1 Heap:
   regions  = 3500
   capacity = 3670016000 (3500.0MB)
   used     = 1735444208 (1655.048568725586MB)
   free     = 1934571792 (1844.951431274414MB)
   47.28710196358817% used
G1 Young Generation:
Eden Space:
   regions  = 1311
   capacity = 2193620992 (2092.0MB)
   used     = 1374683136 (1311.0MB)
   free     = 818937856 (781.0MB)
   62.66730401529637% used
Survivor Space:
   regions  = 113
   capacity = 118489088 (113.0MB)
   used     = 118489088 (113.0MB)
   free     = 0 (0.0MB)
   100.0% used
G1 Old Generation:
   regions  = 249
   capacity = 1357905920 (1295.0MB)
   used     = 241223408 (230.04856872558594MB)
   free     = 1116682512 (1064.951431274414MB)
   17.76436824135799% used

485420 interned Strings occupying 83565264 bytes.

JSTAT -gc 命令的输出:

 S0C    S1C    S0U    S1U      EC       EU        OC         OU       MC     MU    CCSC   CCSU   YGC     YGCT    FGC    FGCT     GCT   
 0.0   33792.0  0.0   33792.0 1414144.0 1204224.0 2136064.0  1558311.7  262872.0 259709.5 19200.0 18531.5  22077  985.995  10     41.789 1027.785
 0.0   33792.0  0.0   33792.0 1414144.0 1265664.0 2136064.0  1558823.7  262872.0 259709.5 19200.0 18531.5  22077  985.995  10     41.789 1027.785
 0.0   63488.0  0.0   63488.0 124928.0 32768.0  3395584.0  1526795.8  262872.0 259709.5 19200.0 18531.5  22078  986.041  10     41.789 1027.830
 0.0   63488.0  0.0   63488.0 124928.0 49152.0  3395584.0  1526795.8  262872.0 259709.5 19200.0 18531.5  22078  986.041  10     41.789 1027.830
 0.0   63488.0  0.0   63488.0 124928.0 58368.0  3395584.0  1526795.8  262872.0 259709.5 19200.0 18531.5  22078  986.041  10     41.789 1027.830

即使是 "JCMD pid VM.native_memory summary" 的输出所产生的总和也约为 5.0GB,甚至不接近 6.3GB。所以我找不到余额 1.3GB 用在哪里了。

我试图找出 6.3GB 是如何与 JVM 实际映射的。所以我决定检查 /proc/pid 文件夹。

在 /proc/pid/status 文件中,

VmRSS   : 6649680 kB 
RssAnon :   6627136 kB
RssFile :     22544 kB
RssShmem:         0 kB 

由此我发现6.3GB的大部分空间都被匿名空间占用了。

PMAP 命令的输出(截断):

Address           Kbytes     RSS   Dirty Mode  Mapping
0000000723000000 3607296 3606076 3606076 rw---   [ anon ]
00000007ff2c0000   12544       0       0 -----   [ anon ]
00007f4584000000     132       4       4 rw---   [ anon ]
00007f4584021000   65404       0       0 -----   [ anon ]
00007f4588000000     132      12      12 rw---   [ anon ]
00007f4588021000   65404       0       0 -----   [ anon ]
00007f458c000000     132       4       4 rw---   [ anon ]
00007f458c021000   65404       0       0 -----   [ anon ]
00007f4590000000     132       4       4 rw---   [ anon ]
00007f4590021000   65404       0       0 -----   [ anon ]
00007f4594000000     132       8       8 rw---   [ anon ]
00007f4594021000   65404       0       0 -----   [ anon ]
00007f4598000000     132       4       4 rw---   [ anon ]
00007f4598021000   65404       0       0 -----   [ anon ]
00007f459c000000    2588    2528    2528 rw---   [ anon ]

我发现第一个匿名地址可能会映射到堆内存,因为它的大小为 3.4GB。但是,我无法找到其余匿名空间的使用情况。

我需要帮助找出 JVM 进程如何使用额外的 1.3 GB。

本机内存跟踪中未提及的有关 JVM 使用的内存的任何信息将不胜感激。

【问题讨论】:

  • 什么代码在这个JVM上运行?尤其是您可能在通过 JNI 或类似方式与 JVM 交互的本机代码中存在泄漏。

标签: java linux memory jvm


【解决方案1】:

正如here 所讨论的,除了本机内存跟踪所涵盖的区域外,还有其他一些事情会在 JVM 进程中消耗内存。

许多大小正好为 64MB 的匿名区域(例如在您的 pmap 输出中)表明这些区域是 malloc arenas。众所周知,标准 glibc 分配器的 issues 内存使用过多,尤其是在具有许多线程的应用程序中。我建议使用jemalloc(或tcmallocmimalloc)作为标准分配器的替代品——它没有提到的泄漏。另一种解决方案是使用 MALLOC_ARENA_MAX 环境变量限制 malloc arenas 的数量。

如果切换到jemalloc 后问题仍然存在,这可能是本机内存泄漏的迹象。例如,Java 应用程序中的本机泄漏可能是由

  • 未关闭的资源/流:ZipInputStreamDirectoryStreamInflaterDeflater 等。
  • JNI 库和代理库,包括标准的jdwp 代理
  • improper bytecode instrumentation

要查找泄漏源,您还可以使用jemalloc 及其内置的profiling feature。但是,jemalloc 无法展开 Java 堆栈跟踪。

async-profiler 可以显示混合的 Java+native 堆栈。虽然它的主要用途是 CPU 和分配分析,但 async-profiler 也可以帮助find native memory leaks 在 Java 应用程序中。

有关详细信息和更多示例,请参阅我的 Memory Footprint of a Java Process 演示文稿。

【讨论】:

  • MALLOC_ARENA_MAX 如果您创建和销毁大量使用某些本机分配(如 Netty)的线程,则可以创造奇迹。我的情况与大量 64MB 区域非常相似。设置 MALLOC_ARENA_MAX=4 摆脱了很多这些。这是一篇很好的文章:dzone.com/articles/…
猜你喜欢
  • 1970-01-01
  • 2011-12-01
  • 1970-01-01
  • 2011-03-03
  • 2019-10-29
  • 2020-07-01
  • 2018-07-11
  • 2012-05-11
  • 1970-01-01
相关资源
最近更新 更多