先提出问题?为什么有时候发现 内存用了很多了,但是没有被回收。手动触发GC后,才回收了很多内存。
JVM设置如下参数:
1. 堆设置2g
2. 新生代设置1g,那么老年代自然也是1g
3. 设置打印GC信息,并输出到 gc.log 文件
-Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:/gc.log
堆信息如下:
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 1073741824 (1024.0MB)
MaxNewSize = 1073741824 (1024.0MB)
OldSize = 1073741824 (1024.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
应用启动后gc日志:
CommandLine flags: -XX:InitialHeapSize=2147483648 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=1073741824 -XX:NewSize=1073741824 -XX:+PrintGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
2023-06-21T14:27:22.324+0800: 1.914: [GC (Metadata GC Threshold) [PSYoungGen: 534779K->13945K(917504K)] 534779K->14033K(1966080K), 0.0344142 secs] [Times: user=0.08 sys=0.00, real=0.04 secs]
2023-06-21T14:27:22.359+0800: 1.948: [Full GC (Metadata GC Threshold) [PSYoungGen: 13945K->0K(917504K)] [ParOldGen: 88K->13799K(1048576K)] 14033K->13799K(1966080K), [Metaspace: 20532K->20532K(1067008K)], 0.0357165 secs] [Times: user=0.21 sys=0.02, real=0.03 secs]
2023-06-21T14:27:24.014+0800: 3.603: [GC (Metadata GC Threshold) [PSYoungGen: 690461K->27760K(917504K)] 704261K->41567K(1966080K), 0.0691493 secs] [Times: user=0.21 sys=0.01, real=0.07 secs]
2023-06-21T14:27:24.083+0800: 3.673: [Full GC (Metadata GC Threshold) [PSYoungGen: 27760K->0K(917504K)] [ParOldGen: 13807K->33241K(1048576K)] 41567K->33241K(1966080K), [Metaspace: 34133K->34133K(1079296K)], 0.0660470 secs] [Times: user=0.46 sys=0.01, real=0.07 secs]
2023-06-21T14:27:27.152+0800: 6.741: [GC (Allocation Failure) [PSYoungGen: 786432K->27067K(917504K)] 819673K->60317K(1966080K), 0.0530390 secs] [Times: user=0.11 sys=0.01, real=0.05 secs]
2023-06-21T14:27:33.056+0800: 12.645: [GC (Metadata GC Threshold) [PSYoungGen: 114700K->12304K(917504K)] 147949K->45561K(1966080K), 0.0118059 secs] [Times: user=0.04 sys=0.00, real=0.02 secs]
2023-06-21T14:27:33.068+0800: 12.658: [Full GC (Metadata GC Threshold) [PSYoungGen: 12304K->0K(917504K)] [ParOldGen: 33257K->21336K(1048576K)] 45561K->21336K(1966080K), [Metaspace: 56024K->56024K(1101824K)], 0.1189569 secs] [Times: user=0.54 sys=0.02, real=0.12 secs]
2023-06-21T14:28:10.725+0800: 50.314: [GC (Allocation Failure) [PSYoungGen: 786432K->5783K(917504K)] 807768K->27128K(1966080K), 0.0070404 secs] [Times: user=0.01 sys=0.01, real=0.01 secs]
一般gc日志格式如下:
[ GC|Full GC (原因) [年轻代:回收前->回收后(空间总大小)] [老年代:回收前->回收后(空间总大小)] 回收前 -> 回收后(堆空间大小) ,[其他空间],耗时 ]
每个空间用 [] 扩起来,不同空间用 ,分割
第一行:因为 Metadata GC Threshold 引发的GC,年轻代 空间进行了GC
第二行:因为 Metadata GC Threshold 引发的 Full GC,年轻代、老年代、元空间 都进行GC
发现总共进行了3次Full GC,元空间 再变大。
由于JVM 元空间默认值21M,所以 配置 -XX:MetaspaceSize=128m,再此启动应用:
2023-06-21T15:18:18.336+0800: 2.364: [GC (Allocation Failure) [PSYoungGen: 786432K->19872K(917504K)] 786432K->19960K(1966080K), 0.0583594 secs] [Times: user=0.14 sys=0.01, real=0.06 secs]
2023-06-21T15:18:21.316+0800: 5.344: [GC (Allocation Failure) [PSYoungGen: 806304K->43337K(917504K)] 806392K->43457K(1966080K), 0.1278580 secs] [Times: user=0.70 sys=0.04, real=0.13 secs]
没有Full GC了,原来是因为 元空间太小,触发了 Full GC。
通过GC日志,可以看出来,每次GC 后面括号里都有原因,像 Allocation Failure。这就是 GC的触发原因,当分配空间失败,也就是 当前内存不足了,就会触发GC。那么 当 内存空间足够分配新对象,即使之前的 对象已经die了,也不随便GC。