JVM如何判断哪些对象可以回收?

JVM如何判断哪些对象可以回收?

作者: 大鱼 2021-03-15 08:11:40

云计算

虚拟化 我们知道在JVM内存中,实例对象基本都是存在于堆中的,那总不能无期限的往里面放吧,一些用不着的对象就需要随时回收掉,这样才能保证这个内存的均衡性,才能保证JVM的正常运行。

[[387518]]

前言

我们上一篇分析的是JVM的内存分布,分为堆内存、虚拟机栈、本地方法栈、方法区以及程序计数器等主要区域;各个区域的特点我也就不啰嗦了,想看的给大家直通车:

大鱼今天在家本来是闲暇的一天,很舒适,结果这个时候,妈妈敲门进来我房间了,咨询我有没有时间帮忙打扫一下父母的房间;(没有时间

当然我不能这么说了,我是个炒鸡孝顺的好孩子,当然了,妈妈,当然有时间了啊,now go,我的乖乖,这么乱的屋子,不对啊,平时都是很干净的啊(内心想逃,后悔,想拒绝

不对啊,妈,为什么房间这么乱啊,这有的东西我也不知道要不要扔掉啊,瞬间难到我了,你们生活中有没有遇到过类似的烦恼?

或者有没有遇到纠结一个东西要不要扔掉的时候,那时候你是如何做的呢?

我们知道在JVM内存中,实例对象基本都是存在于堆中的,那总不能无期限的往里面放吧,一些用不着的对象就需要随时回收掉,这样才能保证这个内存的均衡性,才能保证JVM的正常运行

那么问题来了,JVM如何知道哪些对象该回收、哪些不该回收,就像刚才大鱼不知道爸妈房间哪些东西该收拾、哪些不该收拾一个道理的,其实在JVM中是有两种解决办法的,分别是引用计数法和可达性分析法两种方法,来确定这些对象之中哪些是存活着的、哪些是已经死去的(不可能再被任何途径使用的对象)

问题明白了,下面就是来解决这个问题了,冲吧,干饭人

引用计数算法

这个其实很简单了,重点就是计数;给对象添加一个引用计数器,每引用一次,计数器加一;引用失效的时候,计数器减一;当计数器为0 的时候,则认为不可能被再次使用了;

我觉得不需要大鱼多解释了应该,这个应该及其好理解,但是,这种方法存在一个致命的问题:无法解决对象相互循环引用的问题

解释下这个循环引用问题

一起来看看下面这个例子

  1. public class ReferenceCountingGC { 
  2.  
  3.     public Object instance = null
  4.      
  5.     private byte[] bigSize = new byte[2 * 1024 * 1024]; 
  6.     public static void main(String[] args) { 
  7.         ReferenceCountingGC o1 = new ReferenceCountingGC(); 
  8.         ReferenceCountingGC o2 = new ReferenceCountingGC(); 
  9.         o1.instance = o2; 
  10.         o2.instance = o1; 
  11.         o1 = null
  12.         o2 = null
  13.         //假设在这行发生了GC,o1和o2是否被回收 
  14.         System.gc(); 
  15.     } 

上面例子中o1和o2对象都分别将对方作为自己的属性注入,这也就是形成了所谓的循环引用;最后o1和o2对象都置为null,也就是栈中不再指向堆中的实例对象地址,但是他们还是会互相引用,所以不会被GC回收

再来看个图解版,加深理解

刚new的o1和o2对象是这个样子的:

分别引用了双方之后是这样子的状态:

最后置为null变成这个样子的:

是的,没错,最后就变成了如上图所示的尴尬境地,对象1和对象2在内部互相引用,永远失效不了,导致GC通过引用计数法判断他们的引用计数的时候,永远无法判断为0,也就是无法回收咯,不就造成了内存泄漏了吗

可达性分析法

上面说的引用计数法有缺点,而且这个问题还不小,所以现在使用这种方式来作为判断对象是否存活标准的比较少,多数使用的是另一种,可达性分析法;

先来解释下可达性分析法

基本思路就是通过一系列的”GC Roots“的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径就是引用链,当一个对象到GC Roots没有任何的引用链可达的时候,则证明这个对象是不可用的

什么意思呢?来个白话文版本的,就是选择一系列的基准点,这个点能通过引用链连接到的对象就被认为是可用的,只要是无法到达的,都被认为是不可用的,这个不可用并不一定代表对象死亡,只代表对象无法触达,无法再次引用

这就像递归定义的关系一样,如果只定义了递归项而不定义初始项的话,关系也就无从成立,无从开始;如果初始项定义漏掉了内容的话,递推的结果也会随之而漏掉;

什么是GC Roots

垃圾回收时,JVM会首先找到所有的GC Roots,这个过程叫做枚举根节点,这个过程需要暂停用户线程,也就是stop the world;然后再从GC Roots这些根节点向下搜索,可达的对象保留,不可达的便会回收掉

那么,到底什么是GC Roots呢?

GC Roots就是对象,就是JVM确定当前绝对不能回收的对象,只有找到这种对象,后面的搜索才会有意义,不能被回收的对象所依赖的对象也就必然不能回收

GC Roots是一种特殊的对象,是Java程序在运行过程中所必须的对象,而且必须是根对象

哪些对象可以作为GC Roots

基本可以作为GC Roots的对象基本分为两大类:全局对象和执行上下文;

全局对象

  • 方法区静态属性引用的对象:全局对象的一种,Class对象本身很难被回收,回收的条件也是很苛刻,只要Class不被回收,静态成员不会被回收
  • 方法区常量池引用的对象:全局对象,比如字符串常量池,常量初始化之后不会再次改变

执行上下文对象

  • 方法栈的栈帧本地变量表引用的对象:线程方法执行的时候,会将方法打包成一个栈帧入栈执行,方法里得到的局部变量会存放到本地变量表中,只要方法未执行完,还没出栈,即本地变量表还会被访问,GC不应该回收
  • JNI本地方法栈引用的对象:和上面同样的道理
  • 被同步锁持有的对象:被synchronized锁住的对象不可回收,否则锁就失效了,那锁就没意义了

不可达的对象一定会回收吗?(缓刑阶段)

其实被判定为 不可达的对象,也不一定是”非死不可“的,还有一次复活机会,这时是处于缓刑阶段,要真正宣告一个对象死亡,至少要经历再次标记过程(其实就是finalize方法在搞怪)

我们在电视中也是经常见到类似的场景,一个人被判定死刑了,午时已到,立即执行,一般这个时候就会出来一个飞刀,刀下留人,皇上有旨;也有可能是一个飞刀,直接二话不说,噼里啪啦一顿操作,把人救走,是不是很熟悉

没错,这个过程就是finalize的内部过程,让被判定死刑的犯人”重获新生“

标记的前提是对象在进行可达性分析后发现没有与GC Roots相连接的引用链

第一次标记

筛选的条件是这个对象是否有必要执行finalize()方法;若对象未重写这个方法或者已被虚拟机调用过,虚拟机则认为没有必要执行,对象被回收

第二次标记

若这个对象有必要执行finalize方法,则这个对象会被放到一个F-Queue队列中,并在稍后由虚拟机自动创建的一个低优先级的finalizer线程去执行;

这里的执行指的是虚拟机会触发这个方法,但是不保证运行完成,这样做的原因是这个方法执行缓慢,也可能出现死循环,严重可能会导致回收系统崩溃

finalize是对象逃脱死亡命运的最后一次机会,稍后GC会对F-Queue中的对象进行二次标记,如果在这里面重新和GC Roots挂上引用关系,则可以逃脱被回收的命运;否则,就肯定GG了

方法区的回收

很多人认为方法区没有垃圾回收,Java虚拟机规范中也确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区中的垃圾回收的性价比一般比较低,在上面说的堆中进行一次垃圾回收会回收70—95的空间,而永久代中的垃圾回收的效率远低于此

方法区中的垃圾回收主要是两部分:废弃常量和无用的类;废弃常量的回收和Java堆中的对象类似,不多说了

但是判断一个类是否是无用的类,则条件比较苛刻,需要满足三个条件:

  • 该类的所有实例都已经被回收,即Java堆中无该类的任何实例
  • 加载该类的ClassLoader已经被回收
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问到该类的方法

虚拟机规范中说的是满足上面三个条件,便可以对无用的类进行回收,但是并不是必然回收;是否对类对类进行回收,可以根据虚拟机提供的参数来进行控制

在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGI这类频繁自定义ClassLoader的场景都需要虚拟机具备类的卸载功能,以保证永久代不会溢出

我爱总结

我爱总结之JVM如何判断哪些对象可以回收,总结很重要,整理思路,记得后续的温故而知新,GitHub地址在下面,我会把所有原创技术文章放到上面,持续不断的更新

引用计数法:存在循环引用的致命问题

可达性分析法:以GC Roots作为起点,可以达到的就不可回收,不可达到的暂定认为”死亡“;但是不是非死不可,有通过finalize方法加重新连接引用链的方法,让一个对象重新复活;但是不保证执行完成,这种方法是不靠谱的,也是不建议使用的

好了,以上就是全部内容了,我是小鱼仙,你们的学习成长小伙伴

我希望有一天能够靠写字养活自己,现在还在磨练,这个时间可能会有很多年,感谢你们做我最初的读者和传播者。请大家相信,只要给我一份爱,我终究会还你们一页情的。

 

 

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/251983.html<

(0)
运维的头像运维
上一篇2025-04-29 05:45
下一篇 2025-04-29 05:46

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注