首页 > 系统相关 >内存管理:判断对象是否存活

内存管理:判断对象是否存活

时间:2023-04-03 18:00:38浏览次数:43  
标签:Java 对象 存活 算法 GC 内存 Roots 引用

在堆里面存放着 Java 世界中几乎所有的对象实例,垃圾收集器在对 Java 堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对象)。

有两种判断对象是否存活的算法:引用计数算法、可达性分析算法。

  • 引用计数算法判断对象是否存活的基本思路是:在对象中添加一个引用计数器,每当有一个地方引用该对象时,计数器的值就加一;当引用失效时,计数器的值就减一;任何时刻计数器为零的对象就是不可能再被使用的对象。
  • 可达性分析算法判断对象是否存活的基本思路是:通过一系列被称为 “GC Roots” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为 “引用链”(Reference Chain),如果某个对象到 GC Roots 间没有任何引用链相连(用图论的话来说就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可能再被使用的对象。

引用计数算法

引用计数算法(Reference Counting)判断对象是否存活的基本思路是:在对象中添加一个引用计数器,每当有一个地方引用该对象时,计数器的值就加一;当引用失效时,计数器的值就减一;任何时刻计数器为零的对象就是不可能再被使用的对象。


客观地说,引用计数算法虽然占用了一些额外的内存空间来进行计数,但引用计数算法的原理简单,判定效率也很高,在大多数情况下它都是一个不错的算法。也有一些比较著名的应用案例, 例如微软 COM(Component Object Model)技术、使用 ActionScript3 的 FlashPlayer、Python 语言以及在游戏脚本领域得到许多应用的 Squirrel 中都使用了引用计数算法进行内存管理。

但是,在 Java 领域,至少主流的 Java 虚拟机里面都没有选用引用计数算法进行内存管理,主要原因是,这个看似简单的算法有很多例外情况要考虑,必须要配合大量的额外处理才能保证正确地工作,譬如单纯的引用计数就很难解决对象之间相互循环引用的问题。

举个简单的例子,请看代码清单 3-1 的 testGC() 方法:对象 objA 和 objB 都有字段 instance,赋值令 objA.instance=objB 及 objB.instance=objA,除此之外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,但是因为它们互相引用着对方, 导致它们的引用计数都不为零,引用计数算法也就无法回收它们。

代码清单 3-1 引用计数算法的缺陷

/**
 * testGC()方法执行后, objA和objB会不会被GC呢?
 *
 * @author zzm
 */
public class ReferenceCountingGC {
    public Object instance = null;
    private static final int _1MB = 1024 * 1024;
    /**
     * 这个成员属性的唯一意义就是占点内存, 以便能在GC日志中看清楚是否有回收过
     */
    private byte[] bigSize = new byte[2 * _1MB];

    public static void testGC() {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA.instance = objB;
        objB.instance = objA;
        objA = null;
        objB = null;
		// 假设在这行发生GC, objA和objB是否能被回收?
        System.gc();
    }
}

可达性分析算法

当前主流的商用程序语言(Java、C#,上溯至古老的 Lisp)的内存管理子系统,都是通过可达性分析(Reachability Analysis)算法来判断对象是否存活。

可达性分析算法判断对象是否存活的基本思路是:通过一系列被称为 “GC Roots” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径被称为 “引用链”(Reference Chain),如果某个对象到 GC Roots 间没有任何引用链相连(用图论的话来说就是从 GC Roots 到这个对象不可达)时,则证明此对象是不可能再被使用的对象。

如下图所示,对象 object 5、object 6、object 7 虽然互有关联,但是它们到 GC Roots 是不可达的,因此它们将会被判定为是可回收的对象。

image-20230222100921648.png

在 Java 技术体系里面,固定可作为 GC Roots 的对象包括以下几种:

  • Java 虚拟机栈(栈帧中的本地变量表) 中引用的对象,譬如各个线程调用的方法堆栈中使用到的参数变量(方法定义时声明的变量)引用的对象、局部变量(定义在方法中的变量)引用的对象、临时对象(没有变量引用的对象)等。

  • 本地方法栈中 JNI(即通常所说的 Native 方法)引用的对象(非 Java 代码中的对象)。

  • 方法区中引用的对象:

    • 方法区中类的静态属性(static 关键字)引用的对象,譬如 Java 类的引用类型静态变量。
    • 方法区中常量(static 和 final 关键字)引用的对象,譬如字符串常量池(String Table)里的引用。
  • 所有被同步锁(synchronized 关键字)持有的对象。

  • Java 虚拟机内部的引用,如基本数据类型对应的 Class 对象,一些常驻的异常对象(比如 NullPointExcepiton、 OutOfMemoryError)等,还有系统类加载器。

  • 反映 Java 虚拟机内部情况的 JMXBean、JVMTI 中注册的回调、本地代码缓存等。

除了这些固定的 GC Roots 集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不同,还可以有其他对象 “临时性” 地加入,共同构成完整的 GC Roots 集合。譬如后文将会提到的分代收集和局部回收(Partial GC),如果只针对 Java 堆中某一块区域发起垃圾收集时(如最典型的只针对新生代的垃圾收集),必须考虑到内存区域是虚拟机自己的实现细节(在用户视角里任何内存区域都是不可见的),更不是孤立封闭的,所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用,这个时候就需要将这些关联区域的对象也一并加入 GC Roots 集合中去,这样才能保证可达性分析的正确性。

参考资料

《深入理解 Java 虚拟机》第 3 章:垃圾收集器与内存分配策略 3.2 对象已死?

标签:Java,对象,存活,算法,GC,内存,Roots,引用
From: https://www.cnblogs.com/feiyu2/p/17283806.html

相关文章

  • PA 懒加载(循环引用,N+1,使用关联对象)(二)
    这次具体讲述一下,对于懒加载遇到(循环引用,N+1,使用关联对象)的解决方案。为了方便大家模拟操作,我会完整说一下不想看过程的,直接看总结。 一建表创建School和UserSchoolSETNAMESutf8mb4;SETFOREIGN_KEY_CHECKS=0;--------------------------------Tablestructu......
  • COM对象的创建原理
    笔者学习COM原理有一段时间了,但是在创建COM对象时一直使用的是智能指针,别的方式在网上也见过好几次,但是不明白其原理,所以也只是看看,现在想系统的学一下COM原理,将原来大致浏览的内容重新看了一遍,又有了些新的收获,现在结合自己的理解,整理如下:创建COM对象主要有三个API函数分别是:一:CoG......
  • JVM堆外内存泄漏故障排查记录
    线上JVM堆外内存泄漏问题的排查过程与思路,其中夹带一些「JVM内存分配的原理分析」以及「常用的JVM问题排查手段和工具分享」,希望对大家有所帮助。在整个排查过程中,我也走了不少弯路,但是在文章中我仍然会把完整的思路和想法写出来,当做一次经验教训,给后人参考,文章最后也总结了下内......
  • 27-springboot-thymeleaf内置对象
    1、内置web对象thymaleaf内置的web对象,可以直接在模板中使用,这些对象由#号开头:#request:相当于HttpServletRequest对象,这是Thymeleaf3.x版本,若是Thymeleaf2.x版本使用#httpServletRequest;${#request.getContextPath()}${#request.getAttribute("phone")}#session:相当于H......
  • xcode 中使用手动管理内存要修改的地方
    出现error:AutomaticReferenceCountingIssue:ARCforbidsexplicitmessagesendof'release'xcode4.2中,修改 把Yes修改为NO。大小:18KB查看图片附件......
  • PUG_关于if 判断和循环对象数组的使用
    PUG_关于if判断和循环对象数组的使用#contentiffollowList.length>0eachvalinfollowList.followingGroupsectionimg.avatarUrl(src=`${val.avatarUrl}`,alt='')......
  • fastjson 把json字符串转成对象
    Stringjson="[{\"fid\":0,\"id\":1,\"name\":\"fjk的测试类目一级\"},{\"fid\":1,\"id\":2,\"name\":\"fjk的测试类目二级\"},{\"fid\":88,\"id\":98,\&q......
  • TypeScript 学习笔记 — 基于对象操作的内置类型的使用(十二)
    目录1.Partial转化可选属性(?)2.Required转化必填属性(-?)3.Readonly转化仅读属性(readonly)Mutate(非内置,与Readonly相对)(-readonly)4.Pick挑选所需的属性5.Omit忽略属性在前几章的笔记中,了解了以下几种内置类型:条件类型相关:Exclude排除类型(差集),Extract抽取......
  • Microsoft平台开发,内存特征码识别
    在软件调试的角度看,某种类型的数据都有它特别的特征码,就像以前的病毒,看到特征码就知道是什么类型的病毒我们从16制格式的内存数据中也能猜出某段内存数据是什么相关类型数据,比如位图,文本Ascii码,被free的内存(0xFEEEFEEE),刚被初始化的内存,栈:(0xCCCCCCCC)烫 堆:(0xCDCDCDCD)都有一定......
  • Power BI App Souce所有的自定义视觉对象打包
    PowerBIAppSouce所有的自定义视觉对象提取PowerBI最全487个官网自定义视觉对象提取[Date:2023/04/03]官网地址:BusinessApps–MicrosoftAppSourcePowerBI自定义视觉对象的PBIVIZ和示例PBIX文件:   下载地址:PBICustomVisuals.zip ......