Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 死亡对象判断方法

文章目录

    • 垃圾回收机制
    • 死亡对象判断方法
      • 引用计数法
      • 可达性分析算法
        • 可以作为 GC Roots 的对象
        • 判断对象被回收需要经历的过程
      • 引用类型
        • 引用汇总
        • 引用队列
      • 废弃常量
        • 判定废弃常量
        • 废弃原因
        • 遵循原则
      • 无用的类
        • 所需条件
        • 造成的问题
        • 解决步骤

垃圾回收机制

垃圾回收Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收

死亡对象判断方法

JVM(Java虚拟机)垃圾回收机制 —— 内存分配和回收规则

堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)

引用计数法

给对象中添加一个引用计数器

  • 每当有一个地方引用它,计数器就加 1;
  • 当引用失效,计数器就减 1;
  • 任何时候计数器为 0 的对象就是不可能再被使用的。

这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间循环引用的问题。

![[Pasted image 20231011222409.png]]

所谓对象之间的相互引用问题:除了对象 objAobjB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们

public class ReferenceCountingGc {Object instance = null;public static void main(String[] args) {ReferenceCountingGc objA = new ReferenceCountingGc();ReferenceCountingGc objB = new ReferenceCountingGc();objA.instance = objB;objB.instance = objA;objA = null;objB = null;}
}

可达性分析算法

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

![[Pasted image 20231011222616.png]]

图片来源:JavaGuide

Object 6 ~ Object 10 之间虽有引用关系,但它们到 GC Roots 不可达,因此为需要被回收的对象

可以作为 GC Roots 的对象
  • 虚拟机栈(栈帧中的局部变量表)中引用的对象

  • 本地方法栈(Native 方法)中引用的对象

  • 方法区中类静态属性引用的对象

  • 方法区中常量引用的对象

  • 所有被同步锁持有的对象

  • JNI(Java Native Interface)引用的对象

判断对象被回收需要经历的过程

可达性算法是一种通过判断对象是否可达来确定是否回收的方法。对象被判定为不可达时,并非意味着它们会立即被回收,而是在垃圾回收器执行回收操作时才会被释放。具体的回收时机和策略由垃圾回收器的实现决定

可达性算法中,判断对象是否被回收通常经历以下过程:

  1. 根对象标记:垃圾回收器会从一组称为"根对象(GC Roots)"(如全局变量、活动线程的栈等)开始,标记所有从根对象可直接或间接访问到的对象。这些被标记的对象被认为是"可达"的,应该保留不被回收。

  2. 遍历标记:垃圾回收器会逐个遍历可达对象,标记它们所引用的其他对象。这个过程是递归的:在找到一个对象并标记它后,继续查找并标记被该对象引用的其他对象,直到不再有新对象可访问。

  3. 清理非标记对象:在标记阶段完成后,垃圾回收器会遍历堆内存中的所有对象。未被标记的对象被视为不可达,它们不再被任何可达对象引用或访问。垃圾回收器将这些非标记对象标记为垃圾,并将它们的内存释放出来。

  4. 内存回收:垃圾回收器会执行垃圾回收操作,将被标记为垃圾的对象所占用的内存空间进行回收释放。这个过程通常是自动的,由垃圾回收器负责管理和执行。

引用类型

无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。

JDK1.2 之前,Java 中引用的定义很传统:如果 reference 类型的数据存储的数值代表的是另一块内存的起始地址,就称这块内存代表一个引用

JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用 四种(引用强度逐渐减弱)

引用汇总

1.强引用(Strong Reference)

以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品垃圾回收器绝不会回收它

当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

2.软引用(Soft Reference)

如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存

只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。

3.弱引用(Weak Reference)

如果一个对象只具有弱引用,那就类似于可有可无的生活用品

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue) 联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

4.虚引用(Phantom Reference)

"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收

虚引用主要用来跟踪对象被垃圾回收的活动

虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

特别注意,在程序设计中一般很少使用弱引用与虚引用

使用软引用的情况较多,软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生

引用队列

引用队列(Reference Queue) 是 Java 中用于管理对象引用的特殊队列。它与垃圾回收(Garbage Collection)机制密切相关,用于跟踪和处理被垃圾回收器回收的对象。

当对象被垃圾回收器标记为可回收时,它可能会被加入到引用队列中。通过监视引用队列,我们可以了解对象何时被回收,并进行相应的处理操作

当一个对象的引用类型存在于引用队列中时,我们可以使用 poll() 方法或 remove() 方法从引用队列中获取对应的引用对象。这样,我们就可以得知某个对象何时被垃圾回收器回收了,并进行一些相关的处理工作,比如资源释放、记录日志等。

引用队列在某些场景下非常有用,比如内存敏感的缓存系统、对象终结(Finalization)等。但是需要注意,使用引用队列需要小心处理,避免导致内存泄漏或引起性能问题。

废弃常量

运行时常量池主要回收的是废弃的常量

  • JDK1.7 之前运行时常量池逻辑包含字符串常量池存放在方法区,此时 hotspot 虚拟机对方法区的实现为永久代

  • JDK1.7 字符串常量池被从方法区拿到了堆中,这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区,也就是 hotspot 中的永久代 。

  • JDK1.8 hotspot 移除了永久代用元空间(Metaspace)取而代之,这时候字符串常量池还在堆,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间(Metaspace)

相关知识补充:Java 入门指南:JVM(Java虚拟机) —— Java 内存运行时的数据区域

废弃常量(Deprecated Constants)是指在编程语言或库的API中标记为过时或不推荐使用的常量。它们通常是为了提醒开发者不要再依赖或使用这些常量,并鼓励使用新的替代方案。

假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了。

在Java中,使用@Deprecated注解可以将常量标记为废弃。

public class Constants { @Deprecated public static final int OLD_CONSTANT = 100; public static final int NEW_CONSTANT = 200; // ... }
// 当其他代码中引用此废弃常量时,会收到编译器的警告,以提醒开发者不再使用该常量。
判定废弃常量

要判定一个常量是否被标记为废弃(Deprecated),可以通过以下步骤进行

  1. 查看文档或注释:首先,查看常量的文档或注释,看是否有明确的说明该常量已被废弃。通常,废弃的常量会在文档或注释中标记为废弃,并给出了推荐的替代方案。

  2. 阅读源代码:如果没有明确的文档或注释,可以查看常量所在类的源代码。在常量的定义处,通常使用 @Deprecated 注解将废弃常量标记为废弃。在 Java 中,使用了 @Deprecated 注解的常量会在编译时发出警告。

  3. IDE 提示:在使用现代集成开发环境(IDE)时,常量的废弃状态通常会得到特殊标记或提示。IDE 可能会在代码中给出警告,提示开发者一个常量已被废弃

如果经过以上步骤确认一个常量被标记为废弃,那么开发者应该遵循推荐的做法,并尽量避免使用该废弃常量。替代方案往往会在文档或注释中提到,开发者可以根据推荐的替代方案进行调整和更新代码。

废弃原因

标记常量为废弃的原因可以包括

  • 常量已不再符合设计或功能要求。
  • 常量存在安全风险或潜在问题,不推荐使用。
  • 常量已被更好的替代方案取代,推荐使用新的常量。
遵循原则

开发者在使用废弃常量时应该考虑遵循

  • 尽量避免使用废弃常量,推荐使用替代的、非废弃的常量。
  • 如果必须使用废弃常量,开发者应该了解其存在的问题和风险,并确保理解和处理这些问题。
  • 废弃常量可能在未来的版本中被移除,因此开发者应该及时更新代码,以避免依赖于已移除的常量。

无用的类

无用的类(Unused Classes) 是指在代码中存在但没有被使用或引用的类。这些类可能是代码重构、功能删除或其他变更导致的残留代码。

所需条件

类需要同时满足下面 3 个条件才能算是 无用的类

  • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。

  • 加载该类的 ClassLoader 已经被回收。

  • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是可以,而并不是和对象一样不使用了就会必然被回收

造成的问题

在开发过程中,存在无用的类可能会造成一些问题

  1. 内存占用:无用的类会占用内存空间,增加应用程序的内存消耗。

  2. 代码冗余:无用的类会增加代码库的大小,导致代码冗余。

  3. 可读性下降:存在无用的类会使代码库变得混乱和不易维护。

解决步骤

解决无用类的问题,可以采取以下几个步骤

  1. 代码评审:定期进行代码评审,识别和删除未使用的类。
  2. 搜索引擎工具:使用搜索引擎工具(如IDE中的Find Usages)来检查类的引用情况,找出未被引用的类。
  3. 构建工具插件:使用一些构建工具插件(如ProGuard、Unused、UCDetector等),它们可以帮助 自动检测和删除未使用的类
  4. 清理过程:在代码库中进行定期的清理过程,包括删除未使用的类和其他无效代码。

在删除无用的类之前,应该先进行彻底的测试确保没有任何功能受到影响。一些类可能在特定的场景或条件下使用到,并且可能不容易通过简单的搜索来识别。

修复和删除无用的类有助于提高代码质量、减少资源浪费和简化维护工作。但同时也要谨慎操作,确保不会意外删除有用的类。

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

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

相关文章

【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)

目录 一、插入排序 算法思想 二、插入排序 算法步骤 四、复杂度分析 时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性:稳定算法 五、应用场景 💓 博客主页:C-SDN花园GGbond ⏩ 文章专栏:探索数据结构…

STM32之FMC—扩展外部 SDRAM

文章目录 一、FMC外设介绍二、SDRAM 控制原理1、SDRAM关键参数a、容量、分区b、引脚SDRAM 使用 2、SDRAM芯片IS42S16400J3、SDRAM 控制引脚说明控制逻辑地址控制SDRAM 的存储阵列SDRAM 的命令预充电刷新 W9825G6KH:W9825G6KH引脚 三、STM32F429 FMC四、其他文章打开…

基于ssm的个性化影片推荐系统设计与实现

需要项目源码请联系我,目前有各类成品 毕设 javaweb ssh ssm springboot等等项目框架,源码丰富。 专业团队,咨询就送开题报告,活动限时免费,有需要的朋友可以来咨询。 一、摘要 随着科学技术的飞速发展,社…

Matlab simulink建模与仿真 第十五章(信号源库)

参考视频:simulink1.1simulink简介_哔哩哔哩_bilibili 一、信号源库中的模块概览 注:部分模块在第二章中有介绍,本章不再赘述。 二、from输入源模块 1、From Workspace模块 (1)该模块可从MATLAB工作区、模型工作区…

双指针算法专题(2)

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程(ಥ_ಥ)-CSDN博客 所属专栏: 优选算法专题 想要了解双指针算法的介绍,可以去看下面的博客:双指针算法的介绍 目录 611.有效三角形的个数 LCR 1…

King3399 SDK编译简明教程

该文章仅供参考,编写人不对任何实验设备、人员及测量结果负责!!! 0 引言 文章主要介绍King3399(瑞芯微rk3399开发板,荣品)官方SDK编译过程,涉及环境配置、补丁以及编译过程中注意事…

shiro漏洞复现

目录 shiro介绍框架介绍判断是否使用shiro框架 环境搭建CVE-2010-3863漏洞原理影响版本漏洞复现 CVE-2016-4437漏洞原理影响版本漏洞复现 CVE-2020-1957漏洞原理影响版本漏洞复现 shiro-721拉取环境漏洞原理漏洞复现 shiro介绍 框架介绍 Apache Shiro提供了认证、授权、加密和…

CSARA机械手正反解代码解读和左右手定则应用

前言:前段时间在某鱼上买了一份CSARA的机械臂的程序,拿出来分享一下,并记录一下。说明一下并非是公司的核心代码,我也不搞这个....侵权就删了。 首先简单回顾一下CSARA的正逆解。 根据几何的方法能求出末端在平面坐标系中的xy坐标…

第二百三十五节 JPA教程 - JPA Lob列示例

JPA教程 - JPA Lob列示例 以下代码显示了如何使用Lob注释将字节数组保存到数据库。 LOB在数据库中有两种类型:字符大对象(称为CLOB)和二进制大对象(或BLOB)。 CLOB列保存大字符序列,BLOB列可存储大字节序…

Linux memcg lru lock提升锁性能

内核关于per memcg lru lock的重要提交: f9b1038ebccad354256cf84749cbc321b5347497 6168d0da2b479ce25a4647de194045de1bdd1f1d 计算虚拟地址转换基本机制 为了处理多应用程序的地址冲突, linux 系统在应用中使用了虚拟地址,得益于硬件的…

SpringBoot+vue集成sm国密加密解密

文章目录 前言认识SM2后端工具类实现引入依赖代码实现工具类:SM2Util 单元测试案例1:生成服务端公钥、私钥,前端js公钥、私钥案例2:客户端加密,服务端完成解密案例3:服务端进行加密(可用于后面前…

禹神:一小时彻底搞懂跨域解决方案

1. 浏览器的同源策略 2. 跨域会受到哪些限制 4. CORS 解决 Ajax 跨域问题 exposedHeaders 不加这个,js拿不到这个响应头(浏览器控制台network中能看见,但是js拿不到) 5. JSONP 解决跨域问题 JSOP只能解决get请求 服务端代码 客户端代码 服务端代码升…

卡尔曼滤波中Q和R与噪声的关系

卡尔曼滤波 一种用于估计系统状态的递归滤波器,通过融合传感器测量和系统模型,提供系统状态的最优估计。 Q和R是什么 在卡尔曼滤波中,Q和R分别表示过程噪声和测量噪声的协方差矩阵。 Q Q Q矩阵(过程噪声协方差矩阵)…

LC并联电路在正弦稳态下的传递函数推导(LC并联谐振选频电路)

LC并联电路在正弦稳态下的传递函数推导(LC并联谐振选频电路) 本文通过 1.解微分方程、2.阻抗模型两种方法推导 LC 并联选频电路在正弦稳态条件下的传递函数,并通过仿真验证不同频率时 vo(t) 与 vi(t) 的幅值相角的关系。 电路介绍 已知条件…

人工智能和大模型的简介

文章目录 前言一、大模型简介二、大模型主要功能1、自然语言理解和生成2、文本总结和翻译3、文本分类和信息检索4、多模态处理三、大模型的技术特性1、深度学习架构2、大规模预训练3、自适应能力前言 随着技术的进步,人工智能(Artificial Intelligence, AI)和机器学习(Mac…

建设世界一流财务管理体系【数字化顶层设计】【持续更新】

财务管理是企业管理的中心环节,是企业实现基业长青的重要基础和保障。近年来,中央企业认真贯彻落实党中央、国务院决策部署,高度重视财务管理工作,持续优化管理手段,不断创新管理模式,积极应用先进管理工具…

CSS调整背景

一、设置背景颜色 通过 background-color 属性指定,值可以是十六进制 #ffffff,也可以是rgb(0, 255, 255),或是颜色名称 "red" div {background-color: red; /* 通过颜色名称设置 */background-color: #ff0000; /* 通过十六进制设…

面向对象程序设计之继承(C++)

1.继承的定义 1.1继承的概念 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承 呈现了⾯向…

给虚拟机linux系统安装交叉编译工具链

我们在电脑上写的代码编译生成的是X86架构的二进制文件,只能在X86平台上运行,而开发板是ARM架构因此需要安装交叉编译链工具,这样在电脑上写的代码交叉编译之后生成的是ARM架构的二进制文件。 绿色的字眼是与本文无关的只是这样有助于我们的…

推荐5款AI论文大纲生成器,一键极速生成!

在当今学术研究和写作领域,AI论文大纲生成器的出现极大地提高了写作效率和质量。以下是五款功能强大且全面的AI论文大纲生成器推荐: 一、千笔-AIPassPaper 千笔-AIPassPaper是一款基于深度学习和自然语言处理技术的AI写作助手,旨在帮助用户…