如何触发Full GC?这是面试中的高频题。同样了解Full GC和Minor GC对于理解Java应用程序的性能和内存管理非常重要。
区分 GC
在此之前,我们要搞清楚,什么是Full GC,什么是Minor GC?
Minor GC
Minor GC是Java虚拟机(JVM)中一种部分垃圾回收(Partial Garbage Collection)操作,通常是指对新生代(Young Generation)进行的垃圾回收。
在Java堆内存中,对象通常被划分为新生代和老年代两个部分。新生代用于存放新创建的对象,大部分对象在创建后很快就会变得不可达并被回收。因此,新生代的对象存活时间很短。而老年代则用于存放存活时间较长的对象。
Minor GC主要针对新生代进行回收。它的目标是清理掉不再被引用的对象,并将存活的对象移到老年代。在Minor GC过程中,通常使用的是一种称为“复制”(Copying)的垃圾回收算法。这种算法将新生代分为两个部分:Eden空间和两个Survivor空间。当对象被创建时,它们会被放入Eden空间中。在Minor GC发生时,Eden空间中的存活对象会被移到其中一个Survivor空间中,而非存活对象则被回收。随着垃圾回收的多次迭代,一部分存活对象会被移到另一个Survivor空间中,直到达到一定的阈值后被晋升到老年代。
Minor GC通常会比Full GC快,因为它只关注新生代的对象,而不需要扫描整个堆内存。因此,它的停顿时间通常较短,不会对应用程序造成过大的影响。
Full GC
Full GC(Full Garbage Collection)是Java虚拟机(JVM)中的一种完全垃圾回收操作。与Minor GC只清理新生代(Young Generation)相比,Full GC会清理整个堆内存,包括新生代和老年代。Full GC通常是在新生代和老年代都需要进行垃圾回收时触发的。
Full GC的目标是回收所有未使用的对象,以释放内存空间并减少堆内存的使用。在Full GC过程中,Java虚拟机会暂停应用程序的所有线程,然后扫描整个堆内存,标记和清理所有未使用的对象。Full GC的停顿时间通常比Minor GC长,因为它需要扫描整个堆内存,而不仅仅是一部分。
如何触发Full GC
触发Full GC 执行的情况有如下六种。
1. 旧生代空间不足
只有在新生代对象转入及创建为大对象、大数组时才会出现旧生代空间不足的现象,当执行Full GC 后空间仍然不足,则抛出OutOfMemoryError: Java heap space
所以调优时应尽量做到让对象在Minor GC 阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。
2.永久代(Java8之后是元空间)空间满
永久代中存放的一些class 的信息等,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,此时会执行Full GC。如果经过Full GC 仍然回收不了,那么JVM 会抛出 OutOfMemoryError: PermGen space ,为避免永久代占满造成Full GC 现象,
可采用的方法为增大永久代空间或转为使用CMS GC。
3.CMS遇到promotion failed 和concurrent mode failure
当使用CMS垃圾收起,注意GC 日志中是否有promotion failed 和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
- promotionfailed 是在进行Minor GC 时,
survivor space 放不下、对象只能放入旧生代,而此时旧生代也放不下造成的; - concurrent mode failure 是在执行CMS GC 的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。
不过我们可以增大survivorspace、旧生代空间或调低触发并发GC 的比率,并通过设置- XX:CMSMaxAbortablePrecleanTime=5(单位为ms )来设置CMS在标记-清理阶段的最大中止时间。
4.统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间
这是一个较为复杂的触发情况,Hotspot 为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC 时,做了一个判断,如果之前统计所得到的Minor GC 晋升到旧生代的平均大小
大于旧生代的剩余空间,那么就直接触发Full GC。例如程序第一次触发MinorGC 后,有6MB的对象晋升到旧生代,那么当下一次Minor GC 发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB ,则执行Full GC。当新生代采用PSGC 时,方式稍有不同,PS GC 是在Minor GC 后也会检查,例如上面的例子中第一次Minor GC 后,PS GC 会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。
5.RMI 强制定期进行Full GC
对于使用RMI 来进行RPC 或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过-java-Dsun.rmi.dgc.client.gcInterval=3600000 来设置Full GC 执行的间隔时间或通过-XX:+ DisableExplicitGC 来禁止RMI 调用System.gc。
6.手动进行Full GC
通过 System.gc() 或者 Runtime.getRuntime().gc() 的调用触发Full GC。
总结
Full GC和Minor GC是Java应用程序内存管理的重要组成部分,了解它们的工作原理和影响对于优化应用程序的性能、提高稳定性和可靠性至关重要。
往期文章
金三银四面试题(一):JVM类加载与垃圾回收
金三银四面试题(二):数据库缓存的数据一致性
金三银四面试题(三):JVM内存模型