Java程序中新创建的对象在JVM中的堆内存中分配空间,堆内存是线程共享的,分为两个区域年轻代和老年代,年轻代一般存放一些用完就释放的对象,老年代一般存放常驻内存的对象。
- GC的主要流程
- Minor GC的流程
Minor GC过程中所有工作线程都会暂停,如果使用的是ParNew垃圾回收器,就会根据CPU的核数开启相应数量的垃圾回收线程
- Full GC流程
一般使用CMS(Concurrent Mark Sweep)垃圾回收器进行Full GC 分为四个阶段- 初始标记
暂停所有工作线程,只启用垃圾回收线程,标记老年代中被GC Roots直接引用的对象为存活对象,否则为垃圾对象 - 并发标记
垃圾回收线程和工作线程并发执行,追踪所有GC Roots直接引用或间接引用的对象,并进行标记,此时会有新对象进入老年代,也会有存活对象变为垃圾对象,但在这个阶段我们不去理 - 重新标记
继续暂停工作线程,重新标记那些新进入老年代的对象和并发标记阶段状态改变的对象 - 并发清理
垃圾回收线程和工作线程并发执行,清理所有垃圾对象,但是这个阶段还是并发执行的,也会有一些新对象进入老年代,但我们不去理等到下一次GC再去清理。
- 初始标记
需要注意的有两点
- 由于Full GC和工作线程是并发执行的,在GC过程中会有新对象进入老年代,所以系统必须预留出一部分内存默认是百分之92,如果超过这个阈值就会触发Full GC,保证Full GC过程中不会导致OOM,如果GC过程中老年代已满,就会更换为Serial Old垃圾回收器进行单线程GC,期间所有工作线程都会暂停
- 由于CMS使用的是标记-清理算法,每次GC过后都会导致老年代产生大量内存碎片,所以在每次或几次Full GC后都会暂停工作线程进行内存整理,将存活对象紧凑整理在一起,从而空出一片连续内存空间继续使用。