垃圾回收随着程序的运行,内存中的实例对象和变量占用的内存越来越多。如果系统出现异常,JVM会自动完成垃圾回收工作,主要包括:MinorGC/YoungGC:对新生代进行垃圾回收。MajorGC/OldGC:老年代的垃圾收集。FullGC:对整个Java堆和方法区进行垃圾回收。Java堆区可以分为新生代和老年代,新生代又可以分为Eden区、Survivor1区、Survivor2区。具体的比例参数可以看这张图。垃圾回收原理一般情况下,新创建的对象会被分配到Eden区(一些大对象会被特殊处理),这些对象在第一次MinorGC后如果还活着,就会被移到Survivor区。对象在Survivor区每经历一次MinorGC,其年龄就会增加1年,当年龄增长到一定程度时,就会被移到老年代。因为新生代中的对象基本都是生死(80%以上),所以新生代中的垃圾回收算法采用的是复制算法。复制算法的基本思想是将内存分成两块,只使用其中的一块,当这块内存用完后,将存活的对象复制到另一块。复制算法不会产生内存碎片。GC开始时,对象只会存在于名为“From”的Eden区和Survivor区,而“To”的Survivor区为空。紧接着GC之后,Eden区的所有存活对象都会被复制到“To”,而在“From”区域,则根据年龄值判断存活对象。年龄达到一定值(年龄阈值,可通过-XX:MaxTenuringThreshold设置)的对象将被移至老年代,未达到阈值的对象将被复制到“To”区域。这次GC之后,Eden区和From区都被清空了。这时候“From”和“To”就会互换角色,即新的“To”就是上次GC之前的“From”,新的“From”就是上次GC之前的“To”。在任何情况下,名为To的Survivor区域都保证为空。MinorGC会重复这个过程,直到“To”区域被填满,“To”区域被填满后,所有对象都会被移动到老年代。垃圾回收算法mark-clear算法就是标记无效的对象,然后清除它们。标记复制算法是将Java堆分成两部分,每次垃圾回收只使用其中的一部分,然后将所有存活的对象移动到另一个区域。mark-collat??ion算法是一种折衷的垃圾收集算法。在对象标记的过程中,步骤与前两个相同。但是标记之后,存活的对象会被移动到堆的一端,然后可以直接清理存活对象以外的区域。这样就避免了内存碎片,也没有堆空间的浪费。但是,每次进行垃圾回收时,必须暂停所有用户线程,尤其是老年代对象的回收,需要更长的回收时间,对用户体验非常不利。垃圾收集器在JVM中,GC由垃圾收集器执行。因此,在实际应用场景中,我们需要选择合适的垃圾收集器。下面就来介绍一下垃圾收集器。串行收集器串行收集器是最基本也是最古老的收集器。它是一个单线程收集器。使用Serial收集器时,无论是MinorGC还是FullGC,在清理堆空间时,所有的应用线程都会被挂起。ParNew收集器ParNew收集器本质上是Serial收集器的多线程并行版本。除了同时使用多个线程进行垃圾收集外,其余行为包括Serial收集器可用的所有控制参数、收集算法、StopTheWorld、Object分配规则、回收策略等与Serial收集器完全一致.ParallelScavenge收集器ParallelScavenge收集器也是新一代收集器,基于标记复制算法。可以并行收集的多线程收集器与ParNew非常相似。ParallelScavenge收集器的目标是实现一个可控的吞吐量(Throughput)。所谓吞吐量就是处理器花在运行用户代码上的时间与处理器消耗的总时间的比值。如果虚拟机完成一个任务,用户代码加上垃圾回收一共需要100分钟,垃圾回收需要1分钟,那么吞吐量就是99%。SerialOld收集器SerialOld是Serial收集器的老版本,也是单线程收集器,使用标记-排序算法。ParallelOld收集器ParallelOld是ParallelScavenge收集器的老版本,支持多线程并发收集,基于mark-sort算法实现。CMS收集器CMS收集器设计的初衷是消除Parallel收集器和Serial收集器的Fullgc循环中的长时间停顿。CMS收集器在Minorgc时会挂起所有应用线程,以多线程的方式进行垃圾收集。G1收集器G1垃圾收集器是Java7update4之后引入的一种新的垃圾收集器。G1是分代的、增量的、并行的、并发的标记复制垃圾收集器。它的设计目标是适应不断扩大的内存和越来越多的处理器,进一步减少停顿时间,同时兼顾良好的吞吐量。一张图总结了各种GC收集器的对比:
