美文网首页JVM
【JVM】垃圾回收机制(1)--如何判定对象可以回收

【JVM】垃圾回收机制(1)--如何判定对象可以回收

作者: 叨唧唧的 | 来源:发表于2021-07-02 09:56 被阅读0次

一 垃圾回收机制概述

GC即垃圾收集机制是指JVM用于释放那些不再使用的对象所占用的内存。Java语言并不要求JVM有GC,

也没有规定GC如何工作。不过常用的jvm都有GC,而且大多数GC都使用类似的算法管理内存和执行收集操作。

在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能。有些垃圾收集专用于特殊的应用程序。

比如,实时应用程序主要是为了避免垃圾收集中断,而大多数OLTP应用程序则注重整体效率。

理解了应用程序的工作负荷和JVM支持的垃圾收集算法,便可以进行优化配置垃圾收集器。

垃圾收集的目的在于清除不再使用的对象。

GC主要考虑三件要完成的事情:

1)哪些内存需要回收?(对象是否可以被回收的两种经典算法: 引用计数算法和可达性分析算法)

2)什么时候回收?(堆的新生代、老年代、永久代的垃圾回收时机,MinorGC 和 FullGC)

3)如何回收?(经典垃圾回收算法(标记清除算法、复制算法、标记整理算法)及分代收集算法和七种垃圾收集器)

这里主要讨论哪些内存需要回收。
二 如何确定对象是否存活

垃圾收集器对堆进行回收前,首先需要确定哪些对象还"活着",哪些对象已经"死去"。

常用两种算法分析对象是否存活: 引用计数算法和可达性分析算法。
1、引用计数算法
算法思路:

1)给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值加1;

2)当引用失效时,计数器值就减1;

3)任何时刻计数器为0的对象就是不可能再被使用的;
算法优点:

实现简单,判定效率高;
算法缺点:

很难解决对象之间相互循环引用的问题。所以主流的虚拟机里没有选用引用计数算法来管理内存的。

关于循环引用的例子,比如对象objA和objB都有字段instance,赋值令objA.instance = objB,

objB.instance = objA,除此之外对象再无任何引用,实际上两个对象已经不可能再被访问,

但是他们因为相互引用着对方,导致它们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。
循环引用代码:

package com.jpeony.jvm.gc;
 
/**
 * VM Args: -XX:+PrintGCDetails
 * 参数描述: 用于打印GC日志,可以指定GC文件目录-XX:+PrintGCDetails -Xloggc:d:\gc.log
 * 
 * @author yihonglei
 */
public class ReferenceCountingGC {
    public Object instance = null;
    private static final int _MB = 1024 * 1024;
    private byte[] bigSize = new byte[2 * _MB];
 
    public static void testGC() {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA.instance = objB;
        objB.instance  = objA;
 
        objA = null;
        objB = null;
 
        System.gc();
    }
 
    public static void main(String[] args) {
        testGC();
    }
}

GC详情:

从运行结果可以看到内存回收了,说明虚拟机不是用的引用计数算法管理内存。
2、可达性分析算法
算法思路:

通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始往下搜索,搜索所有走过的路径

称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

如图:

可达性分析算法判定对象是否可以回收的图例中,绿色部分为仍然存活的对象,黄色部分判定可回收的对象。

黄色部分object5、object6、object7虽然相互关联,但是它们到GC Roots是不可达的,也就是没有与GC Roots

建立引用链,所以它们将被判定为是可以回收的对象。

在Java语言中,可以作为GC Roots的对象包括下面几种:

1)虚拟机栈(栈帧中的本地变量表)中引用的对象。

2)方法区中静态属性引用的对象。

3)方法区中常量引用的对象,在jdk7后被移到堆中。

4)本地方法栈中JNI(即一般说的Native方法)引用的对象。
算法优点:

更加精确和严谨,可以分析出循环数据结构相互引用的情况;
算法缺点:

1)实现比较复杂;

2)需要分析大量数据,消耗大量时间;

3)分析过程需要GC停顿(引用关系不能发生变化),即停顿所有Java执行线程(称为"Stop The World",

是垃圾回收重点关注的问题);
三、再谈引用

在JDK1.2以前的引用定义:

在JDK1.2以前,Java中的引用定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存

的起始地址,就称这块内存代表着一个引用。这种定义太过狭隘,一个对象在这种定义下只有被引用或者没有

被引用两种状态,一点犹豫的机会都没有,对于哪种"食之无味,弃之可惜"的鸡肋对象就没法描述了。但是,

我们又希望能描述这类对象,当内存空间还足够时,则能保留在内存中;如果内存空间在进行垃圾收集后还是

非常紧张,则可以抛弃这些对象。

在JDK1.2之后对引用做了补充:

在JDK1.2之后,Java对象引用概念进行了扩充,将引用分为强引用(Strong Reference)、

软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4中,

引用强度依次逐渐减弱。

1)强引用(Strong Reference)

强引用在程序里面使用最普遍,只要对象强引用还存在,GC就不会回收掉被引用的对象。比如:

Object strongReference = new Object()

当Java内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象

来解决内存不足的问题。 如果强引用对象不使用时,需要释放引用,让GC能够回收 ,比如:

strongReference = null;

通过StrongReference类来实现软引用。 

2)软引用(Soft Reference)

软引用是用来描述一些还有用但是并非必需的对象。如果一个对象具有软引用,当内存空间足够时,垃圾回收器就不会

回收该对象,但是当内存空间不足时,这些对象就会被回收掉。只要这些对象没有被GC,则这些对象可以被程序使用。

软引用可以用来实现内存敏感的高速缓存,因为通过回收特性,不会影响Java内存,是一个非常有自知之明的引用。

通过SoftReference类来实现软引用。

3)弱引用(Weak Reference)

弱引用也是用来描述非必需的对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象

只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收

掉只被弱引用关联的对象。

通过WeakReference类来实现弱引用。

4)虚引用(Phantom Reference)

虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,

完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例,在任何时候都可能被回收掉。

为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知,用于追踪

对象被GC的活动。

通过PhantomReference类来实现虚引用。

对象引用关系参考:https://juejin.im/post/5b82c02df265da436152f5ad
四 判断对象是生存还是死亡

即使在可达性分析算法中不可达的对象,也并非是"非死不可的",只是暂时处于"缓刑"阶段,

要真正宣告一个对象死亡,至少要经历两次标记过程。

第一次标记:

如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记

并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,

或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为"没有必要执行"。

如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个叫做F-Queue的

队列中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去触发这个方法。

第二次标记:

GC将对F-Queue队列中的对象进行第二次小规模标记;finalize()方法是对象逃脱死亡的最后一次机会:

如果对象在其finalize()方法中重新与引用链上任何一个对象建立关联,第二次标记时会将其移出"即将回收"的集合;

如果对象没有,也可以认为对象已死,可以回收了;

一个对象的finalize()方法只会被系统自动调用一次,经过finalize()方法逃脱死亡的对象,第二次不会再调用;
————————————————
版权声明:本文为CSDN博主「街灯下的校草」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yhl_jxy/article/details/80950336

相关文章

  • 浅谈JAVA虚拟机中的GC

    前言 本文从JVM如何判定对象是否需要回收开始分析,再到JVM的几种垃圾回收思想如何产生,最后再来介绍JVM经典的...

  • 【JVM】垃圾回收机制(1)--如何判定对象可以回收

    一 垃圾回收机制概述 也没有规定GC如何工作。不过常用的jvm都有GC,而且大多数GC都使用类似的算法管理内存和执...

  • JVM-垃圾回收

    JVM的垃圾回收机制指的是对堆中的垃圾对象进行回收。那么我将从三个方面来进行学习和整理,即: 对象被判定为垃圾的标...

  • Java(Android)垃圾回收(GC)

    本文我们主要分析Java中的垃圾回收机制: 1、JVM怎么确定哪些对象应该被回收 2、JVM在什么时候执行垃圾回收...

  • JVM垃圾回收机制

    JVM垃圾回收机制 回收步骤 问题 垃圾标记算法 回收对象:堆中的对象 1.引用计数法 1.1 定义: 堆中的...

  • JVM垃圾回收机制

    JVM垃圾回收 整体思维导图 带着问题理解JVM垃圾回收机制 Java为什么需要垃圾回收机制; 回收哪部分垃圾; ...

  • 2.垃圾收集器与内存分配策略(1)

    本章要点 1.如何判定对象死亡2.垃圾回收算法3.垃圾收集器4.引用分类 1.判定对象死亡 要想知道如何垃圾回收,...

  • Java虚拟机笔记 jvm notes(II GC)

    垃圾回收机制 比较好的文章: JVM 优化经验总结 JVM调优总结 1⃣️可回收对象算法 目前查看对象是否需要回收...

  • JVM怎么判断对象是否已死?

    Java虚拟机中有着垃圾回收机制,自动帮我们回收对象,不需要自己手动实现垃圾回收。那么JVM中的垃圾收集器是如何判...

  • 对象是否已死?看看JVM是怎么判断的

    Java虚拟机中有着垃圾回收机制,自动帮我们回收对象,不需要自己手动实现垃圾回收。那么JVM中的垃圾收集器是如何判...

网友评论

    本文标题:【JVM】垃圾回收机制(1)--如何判定对象可以回收

    本文链接:https://www.haomeiwen.com/subject/qstrultx.html