当前位置: 首页 > article >正文

JVM面试题解,垃圾回收之“对象存活判断”剖析

一、JVM怎么判断一个类/对象是不是垃圾?

先来说如何判断一个对象是不是垃圾

最常用的就是引用计数法可达性分析

引用计数法

引用计数法为每个对象维护一个计数器来跟踪有多少个引用指向该对象。每当创建一个新的引用指向某个对象时,计数器加1;每当一个引用失效或被移除时,计数器减1。当计数器降为0时,表示没有其他对象引用它,因此可以安全地将其回收。

引用计数法有个非常大的问题就是循环引用。

循环引用:如果两个或多个对象相互引用形成环状结构,即使这些对象不再被外部使用,它们的引用计数也不会降为0,从而导致内存泄漏。例如,A持有B的引用,B也持有A的引用,但没有任何其他对象引用A或B,这时它们应该被回收,但由于相互引用,它们的引用计数都不会降为0。

那么如果采用了引用计数法,如何解决循环引用呢? 引入弱引用来打破循环引用链。弱引用不会增加对象的引用计数,因此当一个对象仅通过弱引用保持时,它可以被垃圾回收器回收。

可达性分析

可达性分析就是用来判定对象是否存活的。

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

二、说到GC ROOTS,你知道Java中哪些对象可作为GC ROOTS吗?

作为 GC Roots 的对象包括下面几种(重点是前面 4 种):

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象;各个线程调用方法堆栈中使用到的参数、局部变量、临时变量等。
  • 方法区中类静态属性引用的对象;java 类的引用类型静态变量。
  • 方法区中常量引用的对象;比如:字符串常量池里的引用。
  • 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象。
  • JVM 的内部引用( class 对象、异常对象 NullPointException 、 OutofMemoryError ,系统类加载器)。(非重点)
  • 所有被同步锁( synchronized )持有的对象。(非重点)
  • JVM 内部的 JMXBean 、 JVMTI 中注册的回调、本地代码缓存等(非重点)
  • JVM 实现中的“临时性”对象,跨代引用的对象(在使用分代模型回收时只回收部分代的对象)(非重点)

除了这些固定的 GC Roots 集合以外,跟进用户选用的垃圾回收器以及当前回收的内存区域不同,还可能会有其他对象"临时"加入成为 GC Roots 。

三、对象不可达是不是立即被回收死亡?

即使通过可达性分析判断不可达的对象,也不是“非死不可”,它还会处于“缓刑”阶段,真正要宣告一个对象死亡,需要经过两次标记过程

  • 一次是没有找到与 GCRoots 的引用链,它将被第一次标记。
  • 随后进行一次筛选(如果对象覆盖了 finalize ),我们可以在 finalize 中去拯救,俗称对象的自我救赎。

finalize()方法的工作原理

  1. 标记阶段:当垃圾回收器开始运行时,它首先会进行一次可达性分析,找出所有不可达的对象。

  2. Finalization队列:对于那些实现了finalize()方法且尚未被调用过finalize()的不可达对象,JVM不会直接回收它们,而是将这些对象放入一个称为finalization队列的数据结构中。

  3. 调用finalize():随后,一个专门的Finalizer线程会从finalization队列中取出对象,并调用它们的finalize()方法。这为对象提供了一个最后的机会来执行清理代码或甚至重新与程序中的其他部分建立联系(虽然这种做法不推荐)。

  4. 第二次可达性分析:在finalize()方法执行完毕后,垃圾回收器会对这些对象再次进行可达性分析。如果此时这些对象仍然不可达,那么它们将会被真正地回收;否则,如果finalize()方法使对象重新变得可达(例如通过将自身赋值给某个静态变量或全局变量),则该对象就不会被回收。


http://www.kler.cn/a/514992.html

相关文章:

  • WPF实战案例 | C# WPF实现大学选课系统
  • 学生管理系统C++版(简单版)详解
  • 【SpringCloud】黑马微服务学习笔记
  • linux下springboot项目nohup日志或tomcat日志切割处理方案
  • qml OpacityMask详解
  • 人工智能之深度学习_[3] -PyTorch自动微分模块和构建线性回归模型
  • Haskell语言的数据可视化
  • C++17 新特性深入解析:constexpr 扩展、if constexpr 和 constexpr lambda
  • adb 命令使用大全
  • 贪心算法(题3)区间分组
  • 在SQL的SELECT中实现循环查找、双层和多层循环(迭代)查找 SQL如何实现编程语言的for循环查询 MySQL的Select子查询
  • Spring Boot 自定义属性
  • 代码随想录算法训练营第 15 天(树3)| 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和、222.完全二叉树的节点个数
  • #攻防演练#应急响应#对于挖矿的检测以及防御方案
  • PCF8563一款工业级、低功耗多功能时钟/日历芯片
  • ChatGPT大模型极简应用开发-CH3-使用 GPT-4 和 ChatGPT 构建应用程序
  • 大模型:LangChain技术讲解
  • Linux 离线安装php+nginx+ftp
  • ZooKeeper 中的 ZAB 一致性协议与 Zookeeper 设计目的、使用场景、相关概念(数据模型、myid、事务 ID、版本、监听器、ACL、角色)
  • 【Elasticsearch】index.mapping.source.mode
  • 语义分割文献阅读-SegNet:一种用于图像分割的深度卷积编码器-解码器架构(1.13-1.19)
  • 计算机毕业设计hadoop+spark股票基金推荐系统 股票基金预测系统 股票基金可视化系统 股票基金数据分析 股票基金大数据 股票基金爬虫
  • 蓝桥杯真题 - 翻转 - 题解
  • 如何用Python和Dash打造一个智能股票筛选与可视化系统
  • 关于六通道串口服务器详细讲解
  • 手写SOCKET进行HTTP通信