Kis*_*esh 8 java linux memory jvm
我有一个在 64 位 LINUX 上运行的 JAVA 进程,版本为“CentOS Linux 版本 7.3.1611”,RAM 为 7.6GB。
下面是一些使用过的 JVM 标志,
注意:线程栈(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
Run Code Online (Sandbox Code Playgroud)
我尝试使用 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.
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
甚至“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
Run Code Online (Sandbox Code Playgroud)
由此我发现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 ]
Run Code Online (Sandbox Code Playgroud)
我发现第一个匿名地址可能映射到堆内存,因为它的大小为 3.4GB。但是,我无法找到匿名空间的其余部分是如何使用的。
我需要帮助找出 JVM 进程如何使用额外的 1.3 GB。
除了本机内存跟踪中提到的之外,任何有关 JVM 使用的内存的信息都将不胜感激。
正如这里所讨论的,除了本机内存跟踪涵盖的领域之外,JVM 进程中还有其他消耗内存的事情。
许多大小恰好为 64MB 的匿名区域(如您的pmap输出中所示)表明这些是malloc arenas。众所周知,标准 glibc 分配器存在内存使用过多的问题,尤其是在具有许多线程的应用程序中。我建议使用jemalloc(或tcmalloc、mimalloc)作为标准分配器的直接替代品 - 它没有提到的泄漏。另一种解决方案是使用环境变量限制 malloc 区域的数量MALLOC_ARENA_MAX。
如果即使在切换到 后问题仍然存在jemalloc,这可能是本机内存泄漏的迹象。例如,Java 应用程序中的本机泄漏可能是由以下原因引起的
ZipInputStream、DirectoryStream、Inflater、Deflater等。jdwp代理要查找泄漏源,您还可以使用jemalloc其内置的分析功能。但是,jemalloc无法展开 Java 堆栈跟踪。
async-profiler可以显示混合的 Java+本机堆栈。尽管 async-profiler 的主要目的是 CPU 和分配分析,但它也可以帮助查找Java 应用程序中的本机内存泄漏。
有关详细信息和更多示例,请参阅我的Java 进程的内存占用演示文稿。