英文:
Growing Resident Size Set in JVM
问题
以下是翻译好的内容:
我有一个在64位LINUX上运行的JAVA进程,版本为"CentOS Linux release 7.3.1611",内存为7.6GB。
以下是一些已使用的JVM标志,
- -Xmx3500m
- -Xms3500m
- -XX:MaxMetaspaceSize=400m
- -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,
- -Xmx3500m
- -Xms3500m
- -XX:MaxMetaspaceSize=400m
- -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(或tcmalloc、mimalloc)作为标准分配器的替代品——它不会出现上述泄漏问题。另一种替代方案是使用MALLOC_ARENA_MAX
环境变量限制malloc arenas
的数量。
如果切换到jemalloc
后问题仍然存在,这很可能是本地内存泄漏的迹象。例如,Java应用程序中的本地泄漏可能是以下原因导致的:
- 未关闭的资源/流:
ZipInputStream
、DirectoryStream
、Inflater
、Deflater
等。 - 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论