JVM中不断增长的驻留大小集



我有一个JAVA进程在64位LINUX上运行;CentOS Linux 7.3.1611版";具有7.6GB的RAM。

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

  1. -Xmx3500m
  2. -Xms3500m
  3. -XX: 最大MetaspaceSize=400m
  4. -XX: 压缩类空间大小=35m

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

在运行TOP命令时,它观察到6.3GB的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-堆命令的输出:

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进程中还有其他消耗内存的内容。

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

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

  • 未关闭的资源/流:ZipInputStreamDirectoryStreamInflaterDeflater
  • JNI库和代理库,包括标准jdwp代理
  • 不正确的字节码检测

要查找泄漏源,还可以使用带有内置评测功能的jemalloc。但是,jemalloc无法展开Java堆栈跟踪。

异步探查器可以显示混合的Java+本机堆栈。尽管异步探查器的主要目的是CPU和分配评测,但它也可以帮助查找Java应用程序中的本机内存泄漏。

有关详细信息和更多示例,请参阅我的"Java进程的内存足迹"演示。

最新更新