Growing Resident Size Set in JVM

huangapple go评论86阅读模式
英文:

Growing Resident Size Set in JVM

问题

以下是翻译好的内容:

我有一个在64位LINUX上运行的JAVA进程,版本为"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命令时,观察到Java进程占用了6.3GB的内存。

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命令的输出:

正在附加到进程ID 45749,请稍候...
成功附加调试器。
检测到服务器编译器。
JVM版本为25.252-b14

使用线程本地对象分配。
使用33个线程的G1垃圾收集器

堆配置:
   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)

堆使用情况:
G1堆:
   区域数目  = 3500
   容量      = 3670016000 (3500.0MB)
   已使用    = 1735444208 (1655.048568725586MB)
   空闲      = 1934571792 (1844.951431274414MB)
   已使用率  = 47.28710196358817%
G1年轻代:
伊甸园空间:
   区域数目  = 1311
   容量      = 2193620992 (2092.0MB)
   已使用    = 1374683136 (1311.0MB)
   空闲      = 818937856 (781.0MB)
   已使用率  = 62.66730401529637%
幸存者空间:
   区域数目  = 113
   容量      = 118489088 (113.0MB)
   已使用    = 118489088 (113.0MB)
   空闲      = 0 (0.0MB)
   已使用率  = 100.0%
G1老年代:
   区域数目  = 249
   容量      = 1357905920 (1295.0MB)
   已使用    = 241223408 (230.04856872558594MB)
   空闲      = 1116682512 (1064.951431274414MB)
   已使用率  = 17.76436824135799%

占用83565264字节的485420个已编制索引的字符串。

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是如何映射到

英文:

I have a JAVA process running on 64bit LINUX with version "CentOS Linux release 7.3.1611" with 7.6GB of RAM.

Below are some of the used JVM flags,

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

Note : Size of the Thread stack (1MB) and code cache (240MB) are taken as default and JDK version is 1.8.0_252.

While running the TOP command, its observed that 6.3 GB of my RAM is held by the java
process.

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    

I tried to analyse the native memory of JVM using JCMD, JMAP and JSTAT commands.

Output of JMAP -heap command :

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.

Output of JSTAT -gc command :

 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

Even the sum produced by the output of "JCMD pid VM.native_memory summary" is 5.0GB approx which is not even nearest to 6.3GB. So I could not find where the balance 1.3GB was used.

I tried to find how the 6.3GB is actually mapped with JVM. So I decided to inspect /proc/pid folder.

In /proc/pid/status file ,

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

From this I found that most of the 6.3GB space is occupied by the anonymous space.

Output of PMAP command (truncated):

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 ]

I found that first anonymous address might be mapped for heap memory since its size 3.4GB. However, I was not able to find how the rest of the anonymous space was used.

I need help in finding out, how the extra 1.3 GB is used by the JVM process.

Any information on memory used by the JVM other than mentioned in Native Memory Tracking would be appreciated.

答案1

得分: 3

正如在这里讨论的那样,除了由本地内存跟踪(Native Memory Tracking)覆盖的区域之外,在JVM进程中还有其他消耗内存的因素。

许多大小恰好为64MB的匿名区域(就像您的pmap输出中的那样)表明这些很可能是malloc arenas(malloc分配区)。众所周知,标准的glibc分配器存在问题,特别是在具有许多线程的应用程序中,会导致过多的内存使用。我建议使用jemalloc(或tcmallocmimalloc)作为标准分配器的替代品——它不会出现上述泄漏问题。另一种替代方案是使用MALLOC_ARENA_MAX环境变量限制malloc arenas的数量。

如果切换到jemalloc后问题仍然存在,这很可能是本地内存泄漏的迹象。例如,Java应用程序中的本地泄漏可能是以下原因导致的:

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

要找出泄漏的源头,您还可以使用具有内置分析功能jemalloc。然而,jemalloc无法展开Java堆栈跟踪。

async-profiler可以显示混合的Java和本地堆栈。尽管它的主要目的是CPU和分配分析,但async-profiler也可以帮助在Java应用程序中找出本地内存泄漏

有关详细信息和更多示例,请参阅我的Java进程的内存占用演示文稿。

英文:

As discussed here, besides areas covered by Native Memory Tracking, there are other things that consume memory in the JVM process.

Many anonymous regions of exactly 64MB in size (like in your pmap output) suggest that these are malloc arenas. The standard glibc allocator is known to have issues with excessive memory usage, especially in applications with many threads. I suggest using jemalloc (or tcmalloc, mimalloc) as a drop-in replacement for the standard allocator - it does not have the mentioned leak. An alternative solution is to limit the number of malloc arenas with MALLOC_ARENA_MAX environment variable.

If the problem persists even after switching to jemalloc, this is likely a sign of a native memory leak. For example, native leaks in a Java application may be caused by

  • unclosed resources/streams: ZipInputStream, DirectoryStream, Inflater, Deflater, etc.
  • JNI libraries and agent libraries, including the standard jdwp agent
  • improper bytecode instrumentation

To find a source of the leak, you may also use jemalloc with its built-in profiling feature. However, jemalloc is not capable of unwinding Java stack traces.

async-profiler can show mixed Java+native stacks. Although its primary purpose is CPU and Allocation profiling, async-profiler can also help to find native memory leaks in a Java application.

For details and more examples, see my Memory Footprint of a Java Process presentation.

huangapple
  • 本文由 发表于 2020年9月2日 14:05:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/63699573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定