GC流程
对于GC流程里面需要处理的是年轻代和老年代的内存空间,而元空间(永久代)都不在GC范畴。
- 1、现在有一个新对象产生,那么对象一定需要内存空间,于是现在需要为该对象进行内存空间的申请。
- 2、首先会判断伊甸园区是否有内存空间,如果此时有充足内存空间,则直接将新对象保存到伊甸园区。
- 3、但是如果此时伊甸园区的内存空间不足,那么会自动执行Minor GC操作,将伊甸园区无用的内存空间进行清理。清理之后会继续判断伊甸园区空间是否充足?如果充足,则将新的对象直接在伊甸园区进行内存空间分配。
- 4、如果执行Minor GC之后伊甸园区空间依然不足,那么这个时候会进行存活区判断,如果存活区有剩余空间,则将伊甸园区的部分活跃对象保存在存活区,随后继续判断伊甸园区的内存空间是否充足,如果充足,则进行内存空间分配。
- 5、如果此时存活区也没有内存空间了,则继续判断老年区,如果此时老年区的空间充足,则将存活区中的活跃对象保存到老年区,而后存活区应付出现空余空间,随后伊甸园区将部分活跃对象保存地存活区中,最后在伊甸园区为新对象分配内存空间。
- 6、如果这个时候老年代内存空间也满了,那么这个时候将产生Major GC(Full GC)。然后再将存活区中的活跃对象保存到老年区,从而腾出空间,然后再将伊甸园区的部分活跃对象保存到存活区,最后在伊甸园区为新对象分配内存空间。
- 7、如果老年代执行Full GC之后依然空间依然不足,应付产生OOM(OutOfMemoryError)异常。
观察GC过程:
public static void main(String[] args) {
Random random = new Random();
String str = "show GC";
while (true) {
str += str + random.nextInt(999);
str.intern(); // 强制产生垃圾
}
}
内存大小最大设置为10M,方便观察,结果:
[GC (Allocation Failure) [PSYoungGen: 2048K->504K(2560K)] 2048K->887K(9728K), 0.0008724 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2355K->504K(2560K)] 2738K->1275K(9728K), 0.0008424 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2433K->504K(2560K)] 3205K->2371K(9728K), 0.0005561 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2205K->504K(2560K)] 4072K->3642K(9728K), 0.0007300 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2206K->496K(2560K)] 7007K->6176K(9728K), 0.0006085 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 496K->0K(2560K)] [ParOldGen: 5680K->1842K(7168K)] 6176K->1842K(9728K), [Metaspace: 3539K->3539K(1056768K)], 0.0052348 secs] [Times: user=0.08 sys=0.00, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 871K->0K(2560K)] [ParOldGen: 6831K->5021K(7168K)] 7702K->5021K(9728K), [Metaspace: 3541K->3541K(1056768K)], 0.0057354 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 1743K->0K(2560K)] [ParOldGen: 6684K->4188K(7168K)] 8428K->4188K(9728K), [Metaspace: 3541K->3541K(1056768K)], 0.0041574 secs] [Times: user=0.09 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 4188K->4188K(8704K), 0.0002199 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.huoq.test.Test.main(Test.java:22)
[PSYoungGen: 0K->0K(1536K)] [ParOldGen: 4188K->4168K(7168K)] 4188K->4168K(8704K), [Metaspace: 3541K->3541K(1056768K)], 0.0055203 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
PSYoungGen total 1536K, used 61K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 1024K, 5% used [0x00000000ffd00000,0x00000000ffd0f498,0x00000000ffe00000)
from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
ParOldGen total 7168K, used 4168K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 58% used [0x00000000ff600000,0x00000000ffa12140,0x00000000ffd00000)
Metaspace used 3573K, capacity 4502K, committed 4864K, reserved 1056768K
class space used 391K, capacity 394K, committed 512K, reserved 1048576K
Process finished with exit code 1
可以看到年轻代空间不足触发Minor GC,多次Minor GC后空间仍然不足会触发老年代的空间回收,老年代空间分配失败会触发Full GC,多次Full GC之后空间仍然不足就会导致“OutOfMemoryErroy”。
GC策略
一、新生代可用GC策略
-
串行GC
-
并行回收GC
Stop The World:简称“STW”,在年轻代使用并行GC处理的时候,会产生一个“STW”暂停,在进行对象 回收的时候所有其他线程将被暂时性挂起。 -
并行GC
二、老年代可用GC策略
-
串行GC
-
并行GC
-
并发GC(CMS GC)
三、GC策略参数调整:
四、垃圾收集器参数调整策略
五、测试GC策略
测试代码
String str = "showGCStrategy";
while (true) {
str += str + str;
str.intern(); // 强制产生垃圾
}
-
1、默认策略
添加虚拟机参数:-Xmx10m -Xms10m -XX:+PrintGCDetails
运行结果中取出一段Full GC:[Full GC (Ergonomics) [PSYoungGen: 1606K->0K(2560K)] [ParOldGen: 6600K->2755K(7168K)] 8207K->2755K(9728K), [Metaspace: 3514K->3514K(1056768K)], 0.0026280 secs] [Times: user=0.08 sys=0.00, real=0.00 secs]
可以发现默认情况下:
- PSYoungGen:新生代使用的是PS(并行回收策略)
- ParOldGen:老年代使用的是Par(并行GC)
-
2、使用串行GC策略
添加虚拟机参数:-Xmx10m -Xms10m -XX:+UseSerialGC -XX:+PrintGCDetails 运行结果中取出一段:
[GC (Allocation Failure) [DefNew: 1667K->1667K(3072K), 0.0000124 secs] [Tenured: 6102K->2781K(6848K), 0.0024193 secs] 7770K->2781K(9920K), [Metaspace: 3514K->3514K(1056768K)], 0.0024630 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
可以发现:
- DefNew:单线程串行GC
- Tenured:单线程串行GC
-
3、使用并行回收GC策略(其实就是默认回收策略)
添加虚拟机参数:-Xmx10m -Xms10m -XX:+UseParallelGC -XX:+PrintGCDetails 运行结果中取出一段:
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 5969K->5742K(7168K)] 5969K->5742K(9728K), [Metaspace: 3515K->3515K(1056768K)], 0.0062983 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
可以发现:
- PSYoungGen:新生代使用的是PS(并行回收策略)
- ParOldGen:老年代使用的是Par(并行GC)
-
4 、使用并行GC策略
添加虚拟机参数:-Xmx10m -Xms10m -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails 运行结果:
[GC (CMS Initial Mark) [1 CMS-initial-mark: 3860K(6848K)] 5475K(9920K), 0.0001004 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-mark-start] [CMS-concurrent-mark: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-preclean-start] [CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (CMS Final Remark) [YG occupancy: 1614 K (3072 K)][Rescan (parallel) , 0.0001022 secs][weak refs processing, 0.0000110 secs][class unloading, 0.0002467 secs][scrub symbol table, 0.0004242 secs][scrub string table, 0.0000756 secs][1 CMS-remark: 3860K(6848K)] 5475K(9920K), 0.0009058 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [CMS-concurrent-sweep-start] [GC (Allocation Failure) [ParNew: 1667K->2K(3072K), 0.0005861 secs] 5528K->5479K(9920K), 0.0006012 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1724K->1724K(3072K), 0.0000083 secs][ CMS[CMS-concurrent-sweep: 0.001/0.001 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] (concurrent mode failure): 5358K->4132K(6848K), 0.0027794 secs] 7083K->4132K(9920K), [Metaspace: 3515K->3515K(1056768K)], 0.0028236 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [CMS: 4132K->4114K(6848K), 0.0022847 secs] 4132K->4114K(9920K), [Metaspace: 3515K->3515K(1056768K)], 0.0023052 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]
可以清晰的看到CMS策略的执行流程:
- ParNew:新生代使用的是Par(并行GC策略)
- CMS:老年代使用的是CMS(并发GC策略)
这样对于整个程序暂停时间会非常的短暂,适合响应速度快的程序(类似秒杀活动)。
总结:以上这些GC策略都是原始GC策略,所有的GC策略都有一个问题:都要扫描全部的子内存空间。