JVM 内存区域
如果要为新生代
分配 256m
的内存(NewSize
与 MaxNewSize
设为一致),参数应该这样来写:-Xmn256m
;
还可以通过 -XX:NewRatio=<int>
来设置老年代
与新生代
内存的比值。比如以下参数就是设置老年代与新生代内存的比值为 1。也就是说老年代和新生代所占比值为 1:1,新生代占整个堆栈的 1/2。
-XX:NewRatio=1
JDK 1.8 ,方法区(HotSpot 的永久代)被彻底移除了,取而代之是元空间 Metaspace,元空间使用的是本地内存。
-
Metaspace 的初始容量并不是
-XX:MetaspaceSize
设置,无论-XX:MetaspaceSize
配置什么值,对于 64 位 JVM 来说,Metaspace 的初始容量都是 21807104(约 20.8m)。可以参考 Oracle 官方文档 :Specify a higher value for the option MetaspaceSize to avoid early garbage collections induced for class metadata. The amount of class metadata allocated for an application is application-dependent and general guidelines do not exist for the selection of MetaspaceSize. The default size of MetaspaceSize is platform-dependent and ranges from 12 MB to about 20 MB.
-
Metaspace 由于使用不断扩容到
-XX:MetaspaceSize
参数指定的量,就会发生 Full GC,且之后每次 Metaspace 扩容都会发生 Full GC。也就是说,MetaspaceSize 表示 Metaspace 使用过程中触发 Full GC 的阈值,只对触发起作用;如果MaxMetaspaceSize
设置太小,可能会导致频繁 Full GC ,甚至OOM。
jmap
打印heap信息
命令:jmap -heap pid
描述:显示Java堆详细信息
➜ ~ jmap -heap 10439
Attaching to process ID 10439, please wait...
Error attaching to process: sun.jvm.hotspot.runtime.VMVersionMismatchException:
Supported versions are 25.312-b07. Target VM is 25.272-b1
sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.runtime.VMVersionMismatchException:
Supported versions are 25.312-b07. Target VM is 25.272-b1
at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:435)
at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:305)
at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
at sun.jvm.hotspot.tools.Tool.start(Tool.java:185)
at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
at sun.jvm.hotspot.tools.HeapSummary.main(HeapSummary.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.tools.jmap.JMap.runTool(JMap.java:201)
at sun.tools.jmap.JMap.main(JMap.java:130)
Caused by: sun.jvm.hotspot.runtime.VMVersionMismatchException: Supported versions are 25.312-b07.
Target VM is 25.272-b1
at sun.jvm.hotspot.runtime.VM.checkVMVersion(VM.java:227)
at sun.jvm.hotspot.runtime.VM.<init>(VM.java:294)
at sun.jvm.hotspot.runtime.VM.initialize(VM.java:370)
at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:431)
报错原因是系统有多个jdk,可以指定绝对路径。
➜ ~ echo $JAVA_HOME
/usr/local/TencentKona-8.0.4-272
➜ ~ /usr/local/TencentKona-8.0.4-272/bin/jmap -heap 10439
Attaching to process ID 10439, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.272-b1
using thread-local object allocation.
Garbage-First (G1) GC with 8 thread(s)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 536870912 (512.0MB)
NewSize = 1363144 (1.2999954223632812MB)
MaxNewSize = 321912832 (307.0MB)
OldSize = 5452592 (5.1999969482421875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 260046848 (248.0MB)
MaxMetaspaceSize = 268435456 (256.0MB)
G1HeapRegionSize = 1048576 (1.0MB)
Heap Usage:
G1 Heap:
regions = 512
capacity = 536870912 (512.0MB)
used = 226987352 (216.47200775146484MB)
free = 309883560 (295.52799224853516MB)
42.27968901395798% used
G1 Young Generation:
Eden Space:
regions = 207
capacity = 338690048 (323.0MB)
used = 217055232 (207.0MB)
free = 121634816 (116.0MB)
64.08668730650155% used
Survivor Space:
regions = 0
capacity = 0 (0.0MB)
used = 0 (0.0MB)
free = 0 (0.0MB)
0.0% used
G1 Old Generation:
regions = 10
capacity = 198180864 (189.0MB)
used = 9932120 (9.472007751464844MB)
free = 188248744 (179.52799224853516MB)
5.0116443129443615% used
8841 interned Strings occupying 891192 bytes.
统计heap对象
命令:jmap -histo:live pid 描述:显示堆中对象的统计信息
其中包括每个Java类、对象数量、内存大小(单位:字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个 *
前缀。如果指定了live子选项,则只计算活动的对象。
➜ ~ /usr/local/TencentKona-8.0.4-272/bin/jmap -histo:live 10439 |head -n 30
num #instances #bytes class name
----------------------------------------------
1: 23634 2041848 [C
2: 43 1409712 [Ljava.util.concurrent.ForkJoinTask;
3: 1053 1191208 [B
4: 7197 790496 java.lang.Class
5: 23631 567144 java.lang.String
6: 6322 542264 [Ljava.lang.Object;
7: 8725 279200 java.util.concurrent.ConcurrentHashMap$Node
8: 4123 263872 java.nio.DirectByteBuffer
9: 4096 229376 org.apache.flink.core.memory.MemorySegment
10: 2900 201184 [I
11: 4117 164680 sun.misc.Cleaner
12: 4115 131680 java.nio.DirectByteBuffer$Deallocator
13: 3923 125536 java.util.HashMap$Node
14: 5009 80144 java.lang.Object
15: 900 79200 java.lang.reflect.Method
16: 811 77264 [Ljava.util.HashMap$Node;
17: 1304 73024 java.lang.invoke.MemberName
18: 101 66976 [Ljava.util.concurrent.ConcurrentHashMap$Node;
19: 4160 66560 java.util.concurrent.atomic.AtomicBoolean
20: 80 53760 org.apache.flink.shaded.netty4.io.netty.util.internal.shaded.
org.jctools.queues.MpscArrayQueue
21: 1266 50640 java.util.LinkedHashMap$Entry
22: 1160 46400 java.lang.invoke.MethodType
23: 1144 45760 java.lang.ref.SoftReference
24: 1663 41632 [Ljava.lang.Class;
25: 986 39440 com.typesafe.config.impl.SimpleConfigOrigin
26: 1177 37664 java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry
27: 777 37296 java.util.HashMap
打印等待终结的对象
命令:jmap -finalizerinfo pid
描述:打印等待终结的对象信息
➜ ~ /usr/local/TencentKona-8.0.4-272/bin/jmap -finalizerinfo 10439
Attaching to process ID 10439, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.272-b1
Number of objects pending for finalization: 0
生成dump
文件
命令:jmap -dump:format=b,file=heapdump.hprof pid
描述:生成堆转储快照dump文件。
➜ ~ /usr/local/TencentKona-8.0.4-272/bin/jmap -dump:format=b,file=heapdump.hprof 10439
Dumping heap to /root/heapdump.hprof ...
Heap dump file created
以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的,如果指定了live子选项,堆中只有活动的对象会被转储。想要浏览heap dump,你可以使用jhat(Java堆分析工具)读取生成的文件。
jhat
分析dump文件
命令:jhat -J-mx1024m heapdump.hprof
,当dump文件很大时,需要适当调高-mx;
当端口有占用时 jhat -port 7080 heapdump.hprof
可以指定端口;
➜ ~ jhat -J-mx1024m heapdump.hprof
Reading from heapdump.phrof...
Dump file created Tue May 24 21:38:43 CST 2022
Snapshot read, resolving...
Resolving 4346760 objects...
Chasing references, expect 869 dots
Eliminating duplicate references.
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
jhat会启动一个web服务,在web界面上查看dump文件解析的结果。
使用EMA工具,会比jhat更友好的界面;
jinfo
jinfo
$ /usr/local/TencentKona-8.0.10-332/bin/jinfo 1
Attaching to process ID 1, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.332-b1
Java System Properties:
zookeeper.sasl.client = true
java.runtime.name = OpenJDK Runtime Environment
java.vm.version = 25.332-b1
sun.boot.library.path = /usr/local/TencentKona-8.0.10-332/jre/lib/amd64
java.vm.vendor = Tencent
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = OpenJDK 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.dir = /
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_332-b1
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/local/TencentKona-8.0.10-332/jre/lib/endorsed
line.separator =
...
VM Flags:
Non-default VM flags: -XX:ActiveProcessorCount=8 -XX:CICompilerCount=4
-XX:CompressedClassSpaceSize=260046848 -XX:ErrorFile=null
-XX:InitialHeapSize=3103784960 -XX:MaxDirectMemorySize=493921243
-XX:MaxHeapSize=3103784960 -XX:MaxMetaspaceSize=268435456 -XX:MaxNewSize=1034420224
-XX:MinHeapDeltaBytes=524288 -XX:NewSize=1034420224 -XX:OldSize=2069364736 -XX:+PrintGC
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:+UseParallelGC -XX:+UseUTF8UTF16Intrinsics
Command line: -Duser.timezone=GMT+08 -Dlog.level=INFO -Xmx3103113861 -Xms3103113861
-XX:MaxDirectMemorySize=493921243 -XX:MaxMetaspaceSize=268435456 -verbose:gc -XX:+PrintGCDetails
-XX:+PrintGCTimeStamps -XX:ActiveProcessorCount=8 -Dlog4j2.formatMsgNoLookups=true
jstat
jstat -gc
/usr/local/TencentKona-8.0.10-332/bin/jstat -gc 1 1000
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
43520.0 43008.0 0.0 27234.4 922112.0 282128.7 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 282128.7 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 282128.7 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 282128.7 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 282128.7 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 285552.9 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 285992.0 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
43520.0 43008.0 0.0 27234.4 922112.0 285992.0 2020864.0 35542.2 76928.0 73257.9 10368.0 9767.7 9 0.124 3 0.320 0.444
- S0C: S0区0的容量 (kB);
- S1C: S1的容量(kB);
- S0U: S0已用内存 (kB);
- S1U: S1已用内存 (kB);
- EC: 伊甸园区容量 (kB);
- EU: 伊甸园区已用内存 (kB);
- OC: 老年代容量 (kB);
- OU: 老年代已用内存 (kB);
- MC: 元数据区容量 (kB);
- MU: 元数据区已用内存 (kB);
- CCSC: 类压缩区容量 (kB);
- CCSU: 类压缩区已用内存 (kB);
- YGC:年轻代垃圾回收次数;
- YGCT:年轻代垃圾回收时间;
- FGC:老年代垃圾回收次数;
- FGCT:老年代垃圾回收时间;
- GCT:总垃圾回收时间;
jstat -gcutil
/usr/local/TencentKona-8.0.10-332/bin/jstat -gcutil 1 1000
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 63.32 9.70 1.76 95.23 94.21 9 0.124 3 0.320 0.444
0.00 63.32 9.71 1.76 95.23 94.21 9 0.124 3 0.320 0.444
0.00 63.32 9.71 1.76 95.23 94.21 9 0.124 3 0.320 0.444
0.00 63.32 10.92 1.76 95.23 94.21 9 0.124 3 0.320 0.444
0.00 63.32 12.08 1.76 95.23 94.21 9 0.124 3 0.320 0.444
0.00 63.32 12.08 1.76 95.23 94.21 9 0.124 3 0.320 0.444
0.00 63.32 13.03 1.76 95.23 94.21 9 0.124 3 0.320 0.444
- S0:S0区域使用率
- S1:S1区域使用率
- E:伊甸园区使用率
- O:Old Generation使用率,OU/OC
- M:Matespace区使用率,MU/MC
- CCS:压缩类空间使用率
- YGC:年轻代垃圾回收次数
- YGCT:年轻代垃圾回收时间
- FGC:老年代垃圾回收次数
- FGCT:老年代垃圾回收时间
- GCT:总垃圾回收时间
jstack
jstack -l
/usr/local/TencentKona-8.0.10-332/bin/jstack -l 1
一般情况下,线程栈会很长,可以输出到文件中分析.
/usr/local/TencentKona-8.0.10-332/bin/jstack -l 1 > /tmp/1.threaddump
Native Memory Tracking
默认情况下,NMT是处于关闭状态的,我们可以通过设置 JVM 启动参数来开启:-XX:NativeMemoryTracking=[off | summary | detail]
;(启用NMT会导致5% -10%的性能开销)
- off 不跟踪 JVM 本地内存使用情况。如果不指定
-XX:NativeMemoryTracking
选项则默认为off。 - summary 仅跟踪 JVM 子系统(如:Java heap、class、code、thread等)的内存使用情况。
- detail 除了通过 JVM 子系统跟踪内存使用情况外,还可以通过单独的 CallSite、单独的虚拟内存区域及其提交区域来跟踪内存使用情况。
除了在虚拟机运行时获取 NMT 数据,我们还可以通过两个参数:-XX:+UnlockDiagnosticVMOptions
和-XX:+PrintNMTStatistics
,来获取虚拟机退出时内存使用情况的数据(输出数据的详细程度取决于你设定的跟踪级别,如 summary/detail 等)。
-XX:+UnlockDiagnosticVMOptions
:解锁用于诊断 JVM 的选项,默认关闭。-XX:+PrintNMTStatistics
:当启用 NMT 时,在虚拟机退出时打印内存使用情况,默认关闭,需要开启前置参数-XX:+UnlockDiagnosticVMOptions
才能正常使用。
开启 NMT
jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown]
[scale= KB | MB | GB]
jcmd 33561 VM.native_memory summary scale=MB
33561:
Native Memory Tracking:
Total: reserved=5593MB, committed=477MB
- Java Heap (reserved=4096MB, committed=256MB)
(mmap: reserved=4096MB, committed=256MB)
- Class (reserved=1049MB, committed=25MB)
(classes #2428)
(malloc=11MB #2029)
(mmap: reserved=1038MB, committed=14MB)
- Thread (reserved=25MB, committed=25MB)
(thread #26)
(stack: reserved=25MB, committed=25MB)
- Code (reserved=244MB, committed=5MB)
(malloc=1MB #1623)
(mmap: reserved=244MB, committed=4MB)
- GC (reserved=162MB, committed=150MB)
(malloc=12MB #148)
(mmap: reserved=150MB, committed=137MB)
- Internal (reserved=12MB, committed=12MB)
(malloc=12MB #4010)
- Symbol (reserved=4MB, committed=4MB)
(malloc=3MB #13444)
(arena=1MB #1)
各区域说明
主要关注 committed
部分,这部分是已使用的内存;
Java Heap
Java 堆所使用的内存大小;可以使用 -Xms/-Xmx
或 -XX:InitialHeapSize/-XX:MaxHeapSize
等参数来控制初始/最大的大小
Class
Class 主要是类元数据(meta data)所使用的内存空间,即虚拟机规范中规定的方法区。具体到 HotSpot 的实现中,JDK7 之前是实现在 PermGen 永久代中,JDK8 之后则是移除了 PermGen 变成了 MetaSpace 元空间。
基于此,那在启动 JVM 进程的时候设置的 -XX:MaxMetaspaceSize=256M
参数应该可以限制 Class 所使用的内存大小; 但实际情况并非这样,这里 Class 需要设置 -XX:CompressedClassSpaceSize=256M
来控制。
-XX:MaxMetaspaceSize:Metaspace
总空间的最大允许使用内存,默认是不限制。-XX:CompressedClassSpaceSize:Metaspace
中的 Compressed Class Space 的最大允许内存,默认值是 1G,这部分会在 JVM 启动的时候向操作系统申请 1G 的虚拟地址映射,但不是真的就用了操作系统的 1G 内存。
Thread
线程所使用的内存;可以使用参数 -Xss/-XX:ThreadStackSize
设置
Code
JVM 自身会生成一些 native code 并将其存储在称为 codecache 的内存区域中。JVM 生成 native code 的原因有很多,包括动态生成的解释器循环、 JNI、即时编译器(JIT)编译 Java 方法生成的本机代码 。其中 JIT 生成的 native code 占据了 codecache 绝大部分的空间。
codecache reserve 的最大内存是由 -XX:ReservedCodeCacheSize
参数决定的;
codecache commit 的内存是由 -XX:InitialCodeCacheSize
参数决定的。
GC
GC 所使用的内存,就是垃圾收集器使用的数据所占据的内存,例如卡表 card tables、记忆集 remembered sets、标记栈 marking stack、标记位图 marking bitmaps 等等。其实都是一种借助额外的空间,来记录不同内存区域之间引用关系的结构(都是基于空间换时间的思想,否则寻找引用关系就需要诸如遍历这种浪费时间的方式)。GC 这块内存是必须的,也是我们在使用过程中无法压缩的。
Internal
Internal 包含命令行解析器使用的内存、JVMTI、PerfData 以及 Unsafe 分配的内存等等。
直接内存可以由 -XX:MaxDirectMemorySize=128M
参数决定,最后会在 Internal 中体现:
ByteBuffer.allocateDirect(256 * _1M);
// 如果设置 -XX:MaxDirectMemorySize=128M,这里分配 DirectMemory 会失败
Symbol
Symbol 为 JVM 中的符号表所使用的内存,HotSpot中符号表主要有两种:SymbolTable 与 StringTable。
Java 的类在编译之后会生成 Constant pool 常量池,常量池中会有很多的字符串常量,HotSpot 出于节省内存的考虑,往往会将这些字符串常量作为一个 Symbol 对象存入一个 HashTable 的表结构中即 SymbolTable,如果该字符串可以在 SymbolTable 中 lookup(SymbolTable::lookup)到,那么就会重用该字符串,如果找不到才会创建新的 Symbol(SymbolTable::new_symbol)。
除了 SymbolTable,还有 StringTable(StringTable 结构与 SymbolTable 基本是一致的,都是 HashTable 的结构),即我们常说的字符串常量池。HotSpot 也是基于节省内存的考虑为我们提供了 StringTable,我们可以通过 String.intern 的方式将字符串放入 StringTable 中来重用字符串。
-XX:StringTableSize
参数设置 HashTable 的长度;
-XX:SymbolTableSize
参数设置 SymbolTable 的长度。
如果该值设置的过小的话会导致hash 冲突,即使 HashTable 进行 rehash,hash 冲突也会十分频繁;
基线分析
jcmd 33561 VM.native_memory baseline
33561:
Baseline succeeded
jcmd 33561 VM.native_memory summary.diff scale=MB
33561:
Native Memory Tracking:
Total: reserved=5593MB, committed=479MB
- Java Heap (reserved=4096MB, committed=256MB)
(mmap: reserved=4096MB, committed=256MB)
- Class (reserved=1049MB, committed=26MB)
(classes #2428)
(malloc=11MB #2270 +99)
(mmap: reserved=1038MB, committed=14MB)
- Thread (reserved=25MB, committed=25MB)
(thread #26)
(stack: reserved=25MB, committed=25MB)
- Code (reserved=245MB, committed=6MB)
(malloc=1MB #1996 +106)
(mmap: reserved=244MB, committed=5MB)
- GC (reserved=162MB, committed=150MB)
(malloc=12MB #148)
(mmap: reserved=150MB, committed=137MB)
- Internal (reserved=12MB, committed=12MB)
(malloc=12MB #4029 +3)
- Symbol (reserved=4MB, committed=4MB)
(malloc=3MB #13444)
(arena=1MB #1)
- Native Memory Tracking (reserved=1MB, committed=1MB)
(tracking overhead=0MB)
基线分析会输出从 baseline
开始,到现在各区域的大小变化,以及线程数量变化;