京东 北京 java 中级: 哪些情况下的对象会被垃圾回收机制处理掉? 哪些对象可以被看做是 GC Roots 呢?对象不可达,一定会被垃圾收集器回收么?

news2024/10/24 1:57:03

        我同学最近在面试java的岗位, 这是他遇到的某些关于java的JVM中垃圾回收相关的部分的问题, 他来问我, 我特以此文章来解答.

公司  京东

base  北京

面试时间  2024年10月23日16:00:00



        他跟我说, 面试官一上来就问了一个关于JVM的问题, 直接就给他难住了, 问题是 :

 哪些情况下的对象会被垃圾回收机制处理掉? 

  • 我同学:  额 .. 这个我不太清楚, 应该是没有对象引用这个对象的时候, 就会被清理掉吧. 
  • 面试官:  确实如此, 但是我怎么知道 某个对象是否被其他对象引用
  • 我同学:  这个我知道(很自信), 使用的是可达性分析法, 如果一个对象存在与GCRoots的  某个节点的引用链中, 那么这个对象就不会被清理. 
  • 面试官: 那么有哪些可以作为GCRoot呢? 
  • 我同学:  吧唧吧唧(只答了几点, 没答全)
  • 面试官:  那一个对象如果不存在关联的引用链, 那么是否会被清除? 
  • 我同学:  这个不太清楚.... 

         其实, 你也知道, 这就是想问, 哪些对象会在下一次GC的时候, 被垃圾回收器回收掉. 本问题的本质就是想问你, 内存中的对象的状态变成什么样的时候, 才会被垃圾收集器认定为, 下次必拿下你.

         在java中, 显然一个对象如果没有被其他任何对象引用, 那么它就是一个可以被回收的对象, 那么我怎么知道一个对象是否被其他对象引用? 

  • 思路一:  对象枚举搜索, 找到虚拟机中所有的对象, 然后逐个对象去扫描, 看里面有哪个对象引用了这个对象, 如果存在一个对象引用了当前对象, 那么就不需要回收, 否则标记为需要清除. 

        这个方法实现起来非常简单, 但是有一个非常严重的问题, 就是他需要扫描所有的对象, 每个对象都要扫描一次, 那么性能消耗是非常大的. 

  • 思路二 : 引用计数法, 给每一个对象添加一个引用计数器, 每当有地方引用这个对象的时候, 就给计数器+1, 引用失效的时候就-1, 通过这个计数器的引用值是否为0来判断是否为可回收对象. 

        这个方法也相对比较简单, 实现起来也比较简单, 仅仅只是需要占用些许内存空间, 判定的效率也非常高(只用去读取这个计数值, 就可以知道是否是需要回收), 但是这个算法有一个缺点, 就是无法判断相互引用的对象, 如下: 

        可以看到外部已经没有任何对象引用这个instance1和这个instance2了, 但是他们的引用计数值都不为0, 因此就会出现误判. 

        但是也不是说这种方法就不能使用, 只要做好额外的处理, 那么这种方法还是很高效的. 

  • 思路三: 可达性分析法, 当前主流的Java商用程序的内存管理子系统, 都是使用的可达性分析法, 具体思路就是, 借鉴于 枚举搜索法, 从一系列的GC Roots作为起点集, 从起点集中的结点开始往下扫描, 扫描走过的路径就被称为 "引用链", 如果这个对象扫描了GC Roots之后, 出现在一条相关的引用链上, 那么这个对象就不能被回收. 

        图中的绿色的引用关系就可以看作为一个引用链. 

        第三种思路, 就完美的解答了哪些对象会被标记为回收的对象: 利用可达性分析算法,虚拟机会将一些对象定义为 GC Roots,从 GC Roots 出发沿着引用链向下寻找,如果某个对象不能通过 GC Roots 寻找到,虚拟机就认为该对象可以被回收掉

         但是这个问题接下来就是第二个问题, 什么是GC Roots? 哪些对象可以被看做是GC Roots?

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

  1. 虚拟机栈中的本地变量表中引用的对象(每一个线程都有一个私有的虚拟机栈, 虚拟机栈中每次调用一个方法就会创建一个栈帧并且插入到栈中, 这个栈帧中就包括一个变量表, 这个变量表中就可能包含局部变量参数并且引用了这个对象)
  2. 我们知道在方法区中, 存储了类的静态属性, 这个静态属性也可能会引用某个对象.
  3. 方法区中的常量池亦可引用某个对象, 譬如字符串常量池(String Table)里的引用
  4. 本地方法栈同虚拟机栈, 只不过本地方法栈运行的是一些c/c++编写的代码, 这其中也可能包含对象的引用.
  5. 上述所说都是用户运行程序产生的数据, JVM本身内部也包含很多类对象, class对象, 包含对其他对象的引用.
  6. 当线程持有某个对象的锁时(即使用synchronized关键字修饰的代码块或方法), 该对象就被视为活跃的, 并且可能被其他线程通过锁机制进行访问. 因此, 这个对象在垃圾回收过程中不能被轻易回收, 否则可能会导致持有该锁的线程出现异常或死锁等问题

         紧接着面试官就问了, 如果一个对象不存在于GCRoots的任何一个引用链, 那么下次垃圾回收, 它一定会被清除吗? 

        如果你用过ThreadLocal的话, 那么你就会知道, ThreadLocalMap中的ThreadLocal类型的key是弱引用, 如果被回收, 那么ThreadLocalMap中的对应Key将变为null, 但Value仍然保持强引用, 且无法被GC回收, 从而导致内存溢出(内存泄漏). 

        这里出现了一个关键字, 就是弱引用, 由弱引用, 可以引出Java的四大引用.

 Java四大引用是怎么来的?  

        作为一个Java开发, 你应该知道, 有些对象不是缺了就不行, 但是也不是有就一定行, 我们上述讲解的Java, 抛开四大引用的概念, 就只有一个"引用"的概念, 也就是引用了就必然不会被回收, 但是事实上, 很多场景会出现内存不足的情况, 那么就需要把那些虽然引用了, 但是可以不要的对象给清理掉, 以节省内存空间, 因此就出现了四大引用的概念

        四大引用介绍如下: 

  1. 强引用

    • Java中最常见的引用类型,默认声明时使用的就是强引用。
    • 只要存在强引用指向对象,垃圾回收器就永远不会回收该对象,即使内存不足也不会回收,而是直接抛出OutOfMemoryError错误。
    • 强引用是导致Java内存泄漏的主要原因之一。
  2. 软引用

    • 用于描述一些还有用但非必需的对象。
    • 当内存足够时,软引用对象不会被回收;当内存不足时,系统则会回收软引用对象。如果回收后仍然没有足够的内存空间,则会抛出内存溢出异常。
    • 软引用非常适合实现内存敏感的缓存,如网页缓存、图片缓存等。
  3. 弱引用

    • 用于描述那些非必需的对象
    • 无论内存是否足够,只要JVM开始进行垃圾回收,那些被弱引用关联的对象都会被回收。
    • 弱引用非常适合用于临时缓存或临时存储对象,也可以用于解决对象之间的循环引用问题,避免内存泄漏。
  4. 虚引用

    • 所有引用类型中最弱的一个。
    • 一个对象是否有虚引用的存在,完全不会决定对象的生命周期。也无法通过虚引用来获取被引用的对象。
    • 虚引用的主要作用是跟踪垃圾回收过程,在对象被收集器回收时收到一个系统通知。它必须和引用队列一起使用。

         无论是哪一种引用, 只要是被标记为了清除(对象在进行可达性分析后发现没
有与GC Roots相连接的引用链
),  就会被标记一次, 然后进行筛选, 筛选是否可以进行执行finalize()方法, 如果可以执行finalize()方法(该对象覆盖了Object的finalize()方法, 并且还未执行过finalize()方法), 这个对象就会被放入到一个F-Queue的队列中, 等待一个执行线程来执行这个finalize()方法

        请注意这里的执行, 仅仅只是执行, 并不保证一定执行到代码的最后一行并返回, 因为覆盖的finalize()方法中包含用户代码, 如果代码逻辑出现问题, 会在一定程度上造成执行缓慢的情况, 甚至是死循环, 将很可能导致F-Queue队列中的其他对象永久处于等待. 

        只要在执行的过程中, 对象自我拯救成功:  otherObjcet = this; 也就是将自己赋值给另外一个引用变量, 让其存在对应的引用链

        就可以逃脱此次的回收, 直到下一次回收(下一次回收因为finalize方法已经被执行了, 因此直接就会被回收, finalize在对象的生命周期中, 只能被执行一次)

        自我拯救的代码如下: 

public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("yes, i am still alive :)");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize method executed!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }
}

        这样也就完美的回答了面试官的那个问题: 

即使不可达,对象也不一定会被垃圾收集器回收,1)先判断对象是否有必要执行 finalize()
方法,对象必须重写 finalize()方法且没有被运行过。2)若有必要执行,会把对象放到一个
队列中,JVM 会开一个线程去回收它们,这是对象最后一次可以逃逸清理的机会。

        请注意, 很多人认为这个 finalize方法可以用来资源释放, 请务必别这么做, 可以使用try-finally来平替, 因为finalize的执行是完全不可预知的, 作为资源最后释放的操作, 如果执行失败, 就可以出现资源泄漏的问题, 是非常严重的bug. 

        finalize方法仅仅只是因为历史原因, 向C++程序员做出的一种妥协..... 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2222027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构《顺序表》

文章目录 前言一、什么是顺序表?1.1 顺序表的概念1.2 顺序表的建立 二、MyArrayList的实现三、顺序表的方法四、关于顺序表的例子总结 前言 提示:这里涉及到的ArrayList类是一个泛型类,同时后面的很多内容都会涉及到泛型,如果不了…

【蓝队技能】【内网隧道工具流量分析】FRPNPSreGeorgVenom

蓝队技能 FRP&NPS&reGeorg&Venom 蓝队技能总结前言一、FRP1.1 流量分析1.2 特征提取 二 NPS1.1 流量分析1.2 特征提取 三、reGeor1.1 特征提取 四、Venom1.1 特征提取 总结 前言 本文聚焦内网隧道代理技术,涵盖Frp、Nps、Neo-reGeorg及Venom等工具。这些…

潜水定位通信系统的功能和使用方法_鼎跃安全

潜水定位通信系统是保障潜水安全与作业高效的关键设备。它利用先进的声呐、无线电等技术,可精准定位潜水员位置。在水下能实现潜水员之间以及与水面的双向通信,确保信息及时传递。具备高可靠性和稳定性,即使在复杂水环境中也能正常运行。 一、…

Git Push(TODO)

最近经常碰到GIT push不上去的问题。到处求人解决也真是尴尬,想自己看看,所以刚刚在github上建了一个仓,试了下。结果如下: 暂时可能还不行,因为数据都是加密的,没法看到具体GIT的交互信息。。。 后面再想办…

算法的学习笔记—两个链表的第一个公共结点(牛客JZ52)

😀前言 在链表问题中,寻找两个链表的第一个公共结点是一个经典问题。这个问题的本质是在两个单链表中找到它们的相交点,或者说它们开始共享相同节点的地方。本文将详细讲解这个问题的解题思路,并提供一种高效的解决方法。 &#x…

WPFDeveloper正式版发布

WPFDeveloper WPFDeveloper一个基于WPF自定义高级控件的WPF开发人员UI库,它提供了众多的自定义控件。 该项目的创建者和主要维护者是现役微软MVP 闫驚鏵: https://github.com/yanjinhuagood 该项目还有众多的维护者,详情可以访问github上的README&…

Golang | Leetcode Golang题解之第497题非重叠矩形中的随机点

题目: 题解: type Solution struct {rects [][]intsum []int }func Constructor(rects [][]int) Solution {sum : make([]int, len(rects)1)for i, r : range rects {a, b, x, y : r[0], r[1], r[2], r[3]sum[i1] sum[i] (x-a1)*(y-b1)}return Sol…

SpringBoot项目ES6.8升级ES7.4.0

SpringBoot项目ES6.8.15 升级到 ES7.4.0 前言 由于公司内部资产统一整理,并且公司内部部署有多个版本的es集群,所以有必要将目前负责项目的ES集群升级到公司同一版本7.4.0。es6到es7的升级变化还是挺大的,因此在这里做一下简单记录&#xf…

从新手到高手:map和set的使用技巧全攻略(C++)

✨✨小新课堂开课了,欢迎欢迎~✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C:由浅入深篇 小新的主页:编程版小新-CSDN博客 前言: 本章节讲解的map和set底层…

C++新基础类型(C++11~C++20)

本文章记录C11~C20的新基础类型。 1.整数类型long long long long类型是C11引入的。在C标准中规定,long long是一个至少为64位的整数类型。可以使用LL和ULL字面量后缀进行初始化。格式化打印时分别用%lld和%llu。 long long x 12345LL; unsigned long long y 4…

简单的windows java -jar 无法启动jar包解决方法

简单的windows java -jar 无法启动jar包解决方法 1. 问题 我们项目是使用nacos作为注册中心以及配置中心,我们本地使用idea 进行服务配置以及启动发现没有问题,然后我们的服务经过maven install 打包后发布到LINUX服务启动也没有问题,但是我…

Artistic Oil Paint 艺术油画着色器插件

只需轻轻一点,即可将您的视频游戏转化为艺术品!(也许更多…)。 ✓ 整个商店中最可配置的选项。 ✓ 六种先进算法。 ✓ 细节增强算法。 ✓ 完整的源代码(脚本和着色器)。 ✓ 包含在“艺术包”中。 &#x1f…

上拉电阻和下拉电阻在电路中的作用(一)

上拉电阻和下拉电阻在电路中的作用(一) 1.什么是上下拉电阻2.上下拉电阻的作用:2.1.维持输入引脚处于稳定状态。2.2.配合三极管和MOS进行电平转换电路设计2.3.OC、OD电路(Open Collector集电极开路、Open Drain漏电极开路&#xf…

优化UVM环境(九)-将interface文件放在env pkg外面

书接上回: 优化UVM环境(八)-整理project_common_pkg文件 My_env_pkg.sv里不能包含interface,需要将my_intf.sv文件放在pkg之外

Leetcode 1135. 最低成本连通所有城市

1.题目基本信息 1.1.题目描述 想象一下你是个城市基建规划者,地图上有 n 座城市,它们按以 1 到 n 的次序编号。 给你整数 n 和一个数组 conections,其中 connections[i] [x_i, y_i, cost_i] 表示将城市 x_i 和城市 y_i 连接所要的cost_i&…

【scene_manager】与 MoveIt 机器人的规划场景进行交互

scene_manager Scene Manager包是由 Robotnik 创建的 ROS 包,旨在帮助构建和与 MoveIt 机器人的规划场景进行交互。 背景信息 MoveIt 规划场景 是一个用于存储机器人周围世界的表示(外部碰撞)以及机器人自身状态(内部碰撞和当…

LeetCode.102 二叉树的层序遍历

题目描述 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 提示&#xff1a; 树中节点数目在范围 [0, 2000] 内-1000 < Node.val < 1000 解题思路 对二叉树进行层序遍历即可&am…

最好的ppt模板网站是哪个?做PPT不可错过的18个网站!

现在有很多PPT模板网站&#xff0c;但真正免费且高质量的不多&#xff0c;今天我就分享主流的国内外PPT模板下载网站&#xff0c;并且会详细分析这些网站的优缺点&#xff0c;这些网站都是基于个人实际使用经验的&#xff0c;免费站点会特别标注&#xff0c;让你可以放心下载&a…

【OpenAI】第三节(上下文)什么是上下文?全面解读GPT中的上下文概念与实际案例

文章目录 一、GPT上下文的定义1.1 上下文的组成 二、GPT上下文的重要性2.1 提高生成文本的相关性2.2 增强对话的连贯性2.3 支持多轮对话 三、使用上下文改善编程对话3.1 使用上下文的概念3.2 使用上下文改善对话的作用3.3 使用上下文改善对话的方法3.4 案例分析 四、利用历史记…

记录一个容易混淆的 Spring Boot 项目配置文件问题

记录一个容易混淆的 Spring Boot 项目配置文件问题 去年&#xff0c;我遇到了这样一个问题&#xff1a; 在这个例子中&#xff0c;由于密码 password 以 0 开头&#xff0c;当它被 Spring Boot 的 bean 读取时&#xff0c;前导的 0 被自动去掉了。这导致程序无法正确读取密码。…