一篇文章说完Java的垃圾回收过程

想要了解Java的垃圾回收过程首先要理解Java堆的内存模型,简单表示如下:

Java内存结构

从上面的图片可以看出,Java的堆内存可以简单的分为,新生代和老年代,在新生代中有分为三个区域,1个Eden区和2个Survivor区(分别叫from和to),默认比例为8:1

了解了垃圾回收的内存模型,下面就可以看下垃圾回收的过程。

  1. 创建一个新对象,判断是否大于或等于大对象的阈值(JVM用-XX:PretenureSizeThreshold来定义),如果判断为大对象 ,直接进入老年代。

  2. 如果不属于大对象,则优先在新生代Edge区分配内存,如果能够存放,则直接分配内存,对象创建结束,不存在垃圾回收过程

  3. 当Eden没有足够的空间进行分配的时候,虚拟机开始在新生代进行垃圾回收(MinorGC)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    新生代采用复制算法进行回收垃圾,过程如下:

    1. 在MinorGC前会对老年区的剩余空间进行统计,如果剩余区域的和大于新生代的所有对象之和,则开始进行一次`Minor GC`

    2. `Minor GC`采用复制算法进行垃圾回收,具体过程是把Eden区域的存活得对象拷贝到From区域,所有From区域的对象的年龄+1。From中的对象根据对象的年代决定是进入To,还是进入老年代。进入老年代的参数默认是年龄是15,可以通过`-XX:MaxTenuringThreshold`参数设置。

    3. 清空Eden和from区域,把To和from空间进行交换,保证To区域始终未空区域。

    4. 如果老年代剩余的内存空间小于新生代所有的对象,虚拟机会查看是否允许担保失败.

    5. 如果允许,则虚拟机会继续检查可用空间是否大于历次晋升到老年代的平均水平,如果条件成立,则尝试进行一次`MinorGC` ,显然这样回收是`有风险的`, 如果晋升的对象空间大于老年代的剩余空间,则会触发一次`Full GC`

    6. 虚拟机会查看不允许允许担保失败, 则会直接触发`Full GC`
  4. From空间对象晋升为老年代的时候,为了适应更多内存情况,JVM会检查在Survivor空间中相同年龄所有对象的大小综合大于Survivor空间的一半,则年龄大于或等于这个年龄的对象可以直接进入老年代。
  5. 老年代空间不足时、手工调用System.GC()会触发Full GC