天天看点

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

关于JVM的垃圾回收

  • 1、垃圾回收的几个基本问题
    • 1.1、什么场景下该使用什么垃圾回收策略?
    • 1.2、垃圾回收发生在哪些区域?
    • 1.3、对象在什么时候能够被回收?
  • 2、可达性分析
    • 2.1、可达性分析说明图
    • 2.2、什么是根对象
    • 2.3、关于几种引用类型
    • 2.4、关于finalize() 方法
  • 3、三种基本垃圾回收算法
    • 3.1、标记-清除(Mark-Sweep)算法
    • 3.2、标记-整理(Mark-Compact)算法
    • 3.3、复制(Copy)算法
    • 3.4、三种基础垃圾回收算法的对比
  • 4、两种综合垃圾回收算法
    • 4.1、分代收集算法
      • 4.1.1、关于分代收集算法
      • 4.1.2、分代回收对象
      • 4.1.3、不同区域触发垃圾回收的条件
      • 4.1.4、关于堆内存(Heap)的两点注意项
      • 4.1.5、分代收集算法的好处与调优原则
    • 4.2、增量算法

1、垃圾回收的几个基本问题

1.1、什么场景下该使用什么垃圾回收策略?

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法
  • 在对内存要求苛刻的场景:想办法提高垃圾的回收效率,多回收掉一些对象,腾出更多内存。
  • 在CPU使用率高的情况下:降低高并发时垃圾回收的频率,让CPU更多的去执行你的业务,而不是垃圾回收。

1.2、垃圾回收发生在哪些区域?

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法
  • 堆(Heap) 和 方法区线程共享,是垃圾回收需要考虑的区域。
  • 虚拟机栈、本地方法栈以及程序计数器都是线程隔离的,随着线程的创建而创建,随着线程的销毁而销毁。所以不需要考虑垃圾回收。

1.3、对象在什么时候能够被回收?

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法
  • 引用计数法(java 未使用该方法):

    |_ 通过对象的引用计数器来判断该对象是否被引用。当对象间循环引用时,引用技术法就不起作用了。

  • 可达性分析:

    ​ |_ 以根对象(GC Roots)作为起点向下搜索,走过的路径被称为引用链(Reference Chain),如果某个对象到根对象没有引用链相连时,就认为这个对象是不可达的,可以回收。

2、可达性分析

2.1、可达性分析说明图

以下为可达性分析说明图(图 2-1):

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

2.2、什么是根对象

思考( 图2-1 )中的根对象(GC Roots)具体包含了哪些对象?

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  • 方法区中类静态属性引用的对象。
  • 方法区中常量引用的对象。
  • 本地方法栈中JNI(即 Native方法)引用的对象。

需要注意的是:

一个对象即使不可达,也不一定会被回收

当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。

见以下说明图(图 2-2):

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

2.3、关于几种引用类型

  • 强引用(StrongReference)

    |_

    形如 Object obj = new Object() 的引用。通过关键字new创建的对象所关联的引用就是强引用。

    |_

    如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

  • 软引用

    |_

    软引用通过java.lang.SoftReference类实现。形如 SoftReference<String> str = new SoftReference<>("hello")

    |_

    是用来描述一些有用但非必需的对象。

    |_

    软引用关联的对象,只有在内存不足的时候才会回收。

  • 弱引用

    |_

    弱引用通过WeakReference类实现。形如 WeakReference<String> str = new WeakReference<>("hello")

    |_

    弱引用也是用来描述非必需对象的。

    ​      |_

    无论内存是否充足,都会回收被弱引用关联的对象。

  • 虚引用

    |_

    形如 ReferenceQueue<String> queue = new ReferenceQueue<>();

    PhantomReference<String> pha = new PhantomReference<>("hello", queue);

    |_ 不影响对象的生命周期,如果一个对象只有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动,必须和引用队列(ReferenceQueue)配合使用。当垃圾回收器准备回收一个对象时,如果发现他还有虚引用,就会回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

2.4、关于finalize() 方法

  • finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
  • 避免使用finalize()方法,操作不当可能会导致问题。
  • finalize() 优先级低,何时会被调用无法确定,因为什么时间发生 GC不确定。
  • 建议使用 try…catch…finally 来替代 finalize()。

3、三种基本垃圾回收算法

3.1、标记-清除(Mark-Sweep)算法

  • 标记需要回收的对象。
  • 清理掉要回收的对象。
    关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

3.2、标记-整理(Mark-Compact)算法

  • 标记需要回收的对象。
  • 把所有的存活对象压缩到内存的一端。
  • 清理掉边界外的所有空间。
关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

3.3、复制(Copy)算法

  • 把内存分为两块,每次只使用一块。
  • 将正在使用的内存中的存活对象复制到未使用的内存中去,然后清除掉正在使用的内存中的所有对象。
  • 交换两个内存的角色,等待下次回收。
关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

3.4、三种基础垃圾回收算法的对比

回收算法 优点 缺点
标记-清除 实现简单 存在内存碎片、分配内存速度会受影响
标记-整理 无碎片 整理存在开销
复制 性能好、无碎片 内存利用率低

4、两种综合垃圾回收算法

4.1、分代收集算法

4.1.1、关于分代收集算法

分代收集算法的说明:

  • 把内存分成多个区域,不同区域使用不同的回收算法回收对象。

  • 各种商业虚拟机堆内存的垃圾收集基本上都采用了分代收集。

  • 根据对象的存活周期,把内存分成多个区域,不同区域使用不同的回收算法回收对象。

4.1.2、分代回收对象

  • 新生代回收(Minor GC | Young GC)
  • 老年代回收(Major GC)
  • 清理整个堆 (Full GC)
  • 因为 Major GC 执行时,一般会伴随的执行一次 Minor GC ,所以 Major GC 约等于 Full GC

4.1.3、不同区域触发垃圾回收的条件

  • 当伊甸园空间不足时,触发新生代(Minor GC)垃圾回收。
  • 触发老年代(Full GC)垃圾回收的条件:
    • 老年代(Tenured)空间不足
    • 元空间(Metaspace)不足
    • 要晋升到老年代(Tenured)的对象,所占用的空间大于老年代的剩余空间。
    • 显示调用了 System.gc()
      • 建议垃圾回收器执行垃圾回收
      • -XX:+DisableExplicitGC 参数,忽略掉System.gc()的调用

4.1.4、关于堆内存(Heap)的两点注意项

1、新建的对象不一定分配到伊甸园(Eden)

  • 对于大于 -XX:PretenureSizeThreshold,就会直接分配到老年代。

  • 新生代空间不够。

2、对象不一定要达到年龄才进入老年代

  • 动态年龄:如果幸存区(Survivor)空间中多有相同年龄对象大小的总和,大于 Survivor 空间的一半,那么年龄大于等于该年龄的对象就直接进入老年代

4.1.5、分代收集算法的好处与调优原则

关于JVM的垃圾回收(三)1、垃圾回收的几个基本问题2、可达性分析3、三种基本垃圾回收算法4、两种综合垃圾回收算法

4.2、增量算法

  • 增量算法就是每次只收集一小片区域的内存空间的垃圾

.