JVM垃圾回收机制

垃圾回收机制

对象称为垃圾的判断依据

在堆空间和元空间中,GC这条守护线程会对这些空间开展垃圾回收工作,那么GC如何判断这些空间的对象是否是垃圾,有两种算法:

  • 引用计数法

对象被引用,则计数器+1,如果计数器是0,那么对象将被判定为垃圾,于是被回收。但是这种算法没有解决循环依赖的对象,因此JVM目前主流的厂商没有采用这种算法。

  • 可达性分析算法 GC Roots根
    • gc roots根节点:在对象的引用中,会有那么几种对象的变量:来自线程栈中局部变量表中的变量,静态变量,本地方法栈中的变量,这些变量被称为gc roots根节点
    • 判断依据:gc在扫描堆空间中某个节点时,向上遍历,看能不能遍历到gc roots根节点,如果不能,则意味这个对象是垃圾。

JVM垃圾回收机制

对象中的finalize方法

Object类中有一个finalize方法,也就是说任何对象都有一个finalize方法,这个方法是对象被回收前的最后一个救命稻草

  • GC在垃圾对象回收前,先标记对象,被标记的对象的finalize方法被调用
  • 调用finalize方法如果对象被引用,那么第二次标记该对象,被标记的对象将被移除即将被回收的集合,继续存活。
  • 调用finalize方法如果对象没有被引用,那么将会被回收
  • 注意,finalize方法只会被调用一次

对象的逃逸分析

在jdk1.7之前,对象的创建都是在堆上完成的,但是会有个问题,方法中的未被外部访问的对象。

这种对象没有被外部访问,且在堆空间上频繁创建,当方法结束,需要被gc,浪费了性能

所以在1.7之后,就会进行一次逃逸分析(默认开启),于是这样的对象就直接在栈上创建,随方法的出栈而被销毁,不需要进行GC,减轻了垃圾回收的压力。

在栈上分配内存的时候:会把聚合量替换成标量,减少栈空间的开销,也为了防止栈上没有足够连续空间来创建对象。

  • 标量:java中的基本数据类型(不可再分)
  • 聚合量:引用数据类型。

垃圾回收算法

标记清除算法、复制算法、标记整理算法

标记清除算法

JVM垃圾回收机制

复制算法

JVM垃圾回收机制
注:元空间中新生代的幸存者s0、s1区就是使用的复制算法

标记整理算法

JVM垃圾回收机制

标记存活对象,让所有存活对象向一端移动,再清除其后所有可回收内存。

分代收集算法

JVM垃圾回收机制

  • 堆空间被分成了新生代(1/3)和老年代(2/3),新生代中被分成了eden(伊甸园区)(8/10)、survivor1(1/10)、survivor2(1/10)
  • 对象的创建在eden,如果放不下则触发minor gc(轻gc)
  • 对象经过一次minor gc后,存活的对象会被放到survivor区,并且年龄+1
  • survivor区执行复制算法,当对象年龄达到15,进入到老年代。
  • 如果老年代放满,就会触发full gc(重 gc)

在进行minor gc时,什么样的对象会进入老年代:

  • 对象的年龄达到15岁
  • survivor区放不下,survivor中的所有幸存对象全部进入老年代。

对象进入到老年代的条件

  • 大对象直接进入到老年代(减少大对象在s区来回复制的开销):大对象可以通过参数设置大小,多大的对象被认为是大对象。

    -XX:PretenureSizeThreshold

  • 当对象的年龄到达15岁时进入老年代,这个年龄可以通过参数设置

    -XX:MaxTenuringThreshold

  • 根据对象动态年龄判断,如果s区中的对象总和超过了s区中的50%,那么下一次做复制的时候,把年龄大于等于这次最大年龄的对象都一次性全部放入老年代。

  • 老年代空间分配担保机制:在minor gc时,检查老年代剩余可用空间是否大于年轻代里现有的所有对象(包含垃圾)。如果大于等于,则做minor gc。如果小于,看是否配置了担保参数的配置:-XX: -HandlePromotionFailure,如果配置了,那么判断老年代剩余的空间是否小于历史每次minor gc后进入老年代的对象的平均大小。如果是,则直接full gc,减少一次minor gc。如果不是,执行minor gc。如果没有担保机制,直接full gc。

JVM垃圾回收机制

上一篇:关于postgres数据库部署之后,发现不能被外机连接解决办法


下一篇:年轻代和老年代分别适合什么样的垃圾回收算法