前言

因为Java对象主要存放在Java堆里,所以垃圾收集器(Garbage Collection)在对Java堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(不被引用了)。

判断对象是否健在的算法

1.引用计数算法

引用计数算法,很容易理解,在对象中添加一个引用计数器,每有一个地方引用它时,计数器值就加一;当引用失效是,计数器值就减一;任何时刻计数器为零的对象就是不可以能再被使用的对象

引用计数算法的原理简单,判定效率也很高。市面上也确实有一些技术使用的此类算法来判定对象是否存活,像ActionScript 3 的FlashPlayer、Python语言等。但是在主流的Java虚拟机里面都没有选用引用计算法来管理内存,主要是使用此算法时,必须要配合大量的额外处理才能保证正确的工作,例如要解决对象之间的相互循环引用的问题。

public class OneTest {

    public Object oneTest = null;

    private static final int _1MB = 1024 * 1024;

    private byte[] bigSize = new byte[256 * _1MB];

    /**
* 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否有回收过。
*/
@Test
public void testGC(){ OneTest test1 = new OneTest();
OneTest test2 = new OneTest(); test1.oneTest = test2;
test2.oneTest = test1; test1 = null;
test2 = null; // 假设在这行发生GC,test1和test2是否能被回收?
System.gc(); } }

分析代码,test1和test2对象都被设置成了null,在后面发生GC的时候,如果按照引用计数算法,这两个对象虽然都被设置成了null,但是test1引用了test2,test2又引用了test1,所以这两个对象的引用计数值都不为0,所以都不会被回收,但是真正的实际运行结果是,这两个对象都被回收了,这也说明HotSpot虚拟机并不是用引用计数法来进行的内存管理。

2. 可达性分析算法

当前主流的商用程序语言(Java、C#等),都是通过可达性分析(Reachability Analysis)算法来判断对象是否存活的。这个算法的基本思路就是通过一一系列称为“GC Roots” 的根对象作为起始节点集,从这些节点开始根据引用关系向下搜索,搜索走过的的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots 间没有任何引用链相连,或者从GC Roots 到这个对象不可达时,则证明此对象是不可能再被使用的。

如下图,object10、object11、object12这三个对象,虽然互相有关联,但是它们到GC Roots是不可达的,因此它们会被判定为可回收的对象。



在Java程序中,固定可作为GC Roots 的对象包括以下几种:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个现场被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
  • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
  • 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(NullPointException、OutOfMemoryError)等,还有系统类加载器。
  • 所有被同步锁(synchronized关键字)持有的对象。
  • 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

    除了这些固定的GC Roots集合以外,根据垃圾收集器以及当前回收的呢村区域不同,还会有其他对象“临时性”的加入,如果只针对Java堆中某一块儿区域发起垃圾收集时(例如只针对年轻代的垃圾收集),必须考虑到当前区域内的对象是否有被其他区域的对象所引用,这个时候就需要把这些关联区域的对象一并加入GC Roots集合中,来保证可达性分析的正确性。

重申引用

无论是通过引用计数算法判断对象的引用数量,还是通过可达性分析算法判断对象是否引用链可达,判断对象是否存活都和“引用”离不开关系。在JDK1.2之前,Java里对引用的概念是:如果reference类型的数据中存储的数值代表的是另外一块儿内存的地址,就称该reference数据是代表某块内存、某个对象的引用。

在JDK1.2版之后,Java对引用的概念进行了扩充,将引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱。

  • 强引用是最传统的“引用”的定义,指引用复制,即类似
Object obj = new Object()

这种引用关系。无论在任何情况下,只要强引用关系还存在,垃圾收集器就不会回收掉被引用的对象。

  • 软引用是用来描述一些还有用,但非必须的对象。在系统发生内存溢出前,会先对软引用对象进行第二次回收,如果回收后还没有足够的内存,才会抛出内存溢出的异常。
  • 弱引用也是用来描述那些非必须的对象,但是它的强度比软引用更弱一些,弱引用的对象,只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
  • 虚引用也称为“幽灵引用”或“幻影引用”,它是最弱的一种引用关系。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。

判断对象是生是死的过程

即使在可达性分析算法中,判断为不可达的对象,也不是“非死不可”的,要真正宣告一个对象死亡,至少要经历两次标记过程:

  • 如果第一次对象在进行可达性分析后发现与GC Roots 不可达,将进行第一次标记。
  • 随后对此对象进行一次是否有必要执行finalize()方法进行筛选,假如对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,都视为“没有必要执行”。

    如果对象被判定有必要执行finalize()方法,会将对象放置在一个名为F-Queue的队列中,并在由一条由虚拟机自动建立的、低调度的线程区执行它们的finalize()方法。但并不承诺一定会等待它们运行结束。

需要注意的是:任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临第二次回收,它的finalize()方法不会被再次执行。

还有一点就是Java官方已经明确声明不推荐手动调用finalize()方法了,因为它的运行代价高昂,不确定性大,无法保证各个对象的调用顺序,并且finanlize()能做的所有工作,使用try-finally或其他方式都可以做的更好、更及时。

回收方法区

方法区垃圾收集的“性价比”通常比较低,并且方法区回收也有过于苛刻的判定条件。

方法区的垃圾收集主要回收两部分内容:废弃的常量不再使用的类型,回收废弃常量时,如果当前系统没有一个常量的值是当前常量值,且虚拟机中也没有其他地方引用这个常量。如果这个时候发生垃圾回收,常量就会被系统清理出常量池。

判定一个类型是否属于“不再使用的类”的条件就比较苛刻了,要同时满足如下三个条件:

  • 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
  • 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的冲加载等,否则通常很难达成的。
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

同时满足了上述的三个条件后,也只是被允许进行回收了,关于是否要对类型进行回收还要对虚拟机进行一系列的参数设置,这里就不赘述了,感兴趣的可以自己去查询。

深入理解JVM(③)判断对象是否还健在?的更多相关文章

  1. JVM 判断对象已死,实践验证GC回收

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 提升自身价值有多重要? 经过了风风雨雨,看过了男男女女.时间经过的岁月就没有永恒不变 ...

  2. 深入理解JVM(三) -- 对象的内存布局和访问定位

    一 对象的内存布局: 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header),实例数据(Instance Data)和对齐填充(Padding). HotSpot的对 ...

  3. [jvm] -- 判断对象是否死亡篇

    判断对象是否死亡的两种方法 引用计数法 给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1:当引用失效,计数器就减 1:任何时候计数器为 0 的对象就是不可能再被使用的. 优点: 简单 ...

  4. 理解JVM之对象的生命周期

    1.对象的创建 1) 当虚拟机遇到一条new的指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载,解析和初始化过. 2) 在类加载检查通 ...

  5. 深入理解JVM(二)--对象的创建

    Java是一门面向对象的语言,在Java程序运行的过程中,无时无刻都会有对象被创建出来,在程序语言中,创建对象(例如克隆,反序列化)通常仅仅是一个new关键字,但是在虚拟机中是怎样的呢?本文主要了解一 ...

  6. JVM——判断对象的死活

    一.引用计数法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1,任何时刻计数器为0的对象就是不可能再被使用的. 但是它很难解决对象之间相互循环引用的问 ...

  7. 深入理解JVM(4)——对象的创建和访问

    1.对象的创建 在语言层面上,创建对象(例如克隆,反序列化)通常仅仅是一个new关键字而已. 在虚拟机中,对象(文中讨论的对象限于普通 Java 对象,不包括数组和 Class 对象等)的创建过程如下 ...

  8. 2.1.JVM的垃圾回收机制,判断对象是否死亡

    因为热爱,所以坚持. 文章下方有本文参考电子书和视频的下载地址哦~ 这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言   我们 ...

  9. JVM(二) 对象存活判断和垃圾回收算法

    对象的创建 概述 下面简要介绍创建对象的几个重要步骤 : 检查能否在常量池定位到一个类的符号引用,并检查这个符号代表的类是否已被加载,解析和初始化过.如果没有则执行类加载的操作.(即是说对象的引用放在 ...

随机推荐

  1. mysql+redis缓存策略常见的错误

    什么时候应该更新缓存 应该是从数据库读取数据后,再更新缓存,从缓存读取到数据,就不需要再重新写缓存了,一个常见的错误是,每次访问接口都更新缓存,这样的话,如果接口一直有流量,那么db中的数据,就一直没 ...

  2. webpack指南(一)HRM+Tree Shaking

    参考:https://www.cnblogs.com/PasserByOne/p/12084323.html https://blog.csdn.net/qq593249106/article/det ...

  3. centOS 开启服务器后无法访问(大坑啊)

    在开启了nodejs后,发现虽然ssh访问到主机,但是公网不能访问. 一番调试发现程序是正常的,也确实在监听着端口.折腾良久无果,在Vultr上发帖求助.几分钟后Vultr团队的工程师Joshua B ...

  4. PAT 乙级-1025 链表反转

    给定一个常数K以及一个单链表L,请编写程序将L中每K个结点反转.例如:给定L为1→2→3→4→5→6,K为3,则输出应该为3→2→1→6→5→4:如果K为4,则输出应该为4→3→2→1→5→6,即最后 ...

  5. TP5.0验证器使用方法

    比如我要在分类做一个验证器,首先要在你的后台模块先建立一个文件夹,比如我的后台模块是admin,那么你要在admin里面建立一个validate的文件夹然后再建立一个对应的php文件如下图 文件里面的 ...

  6. Istio Sidecar注入原理

    概念 简单来说,Sidecar 注入会将额外容器的配置添加到 Pod 模板中.这里特指将Envoy容器注应用所在Pod中. Istio 服务网格目前所需的容器有: istio-init 用于设置 ip ...

  7. 使用flask实现简单的文件上传

    from flask import Flask, redirect, render_template, request, url_forfrom werkzeug.utils import secur ...

  8. Java IO(七)ByteArrayInputStream 和 ByteArrayOutputStream

    Java IO(七)ByteArrayInputStream 和 ByteArrayOutputStream 一.介绍 ByteArrayInputStream 和 ByteArrayOutputSt ...

  9. django-CBV刨析、模板层

    今日内容概要 CBV源码剖析 模版层 模版语法传值 模版语法之过滤器 模版语法之标签 自定义过滤器.标签及inclusion_tag 模版的继承 模版的导入 FBV与CBV ""& ...

  10. Beta冲刺 —— 总结随笔

    这个作业属于哪个课程 软件工程 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 一.项目预期计划: 1.对于初版小程序的现 ...