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

JVM专题八:JVM如何判断可回收对象

在JVM专题七:JVM垃圾回收机制中提到JVM的垃圾回收机制是一个自动化的后台进程,它通过周期性地检查和回收不可达的对象(垃圾),帮助管理内存资源,确保应用程序的高效运行。今天就让我们来看看JVM到底是怎么定义“垃圾”对象的。

JVM垃圾对象判断方法

JVM在垃圾回收(Garbage Collection简称GC)过程中定义“垃圾”对象,主要是根据对象是否还可达来判断。以下是一些判断对象是否成为垃圾的常见标准:

  1. 引用计数法:这是一种简单的垃圾回收方法,每个对象都有一个引用计数器,每当有引用指向该对象时,计数器加一;每当引用离开作用域或被赋值为null时,计数器减一。当引用计数器为零时,对象被认为是垃圾。但这种方法无法处理循环引用的问题。

  2. 可达性分析:这是现代JVM中常用的垃圾回收方法。从一系列的“GC Roots”开始,所有能够通过引用链到达的对象都被认为是存活的,而那些无法到达的对象则被认为是垃圾。GC Roots通常包括:

    • 虚拟机栈(栈帧中的本地变量表)中的引用对象
    • 方法区中的类静态属性引用的对象
    • 运行时常量池中引用的对象
    • JNI(Java Native Interface)的引用对象
  3. 不可达的对象:即使对象不可达,JVM也不会立即回收它们。JVM会将这些对象标记为“即将回收”的状态。在下一次垃圾回收周期中,这些对象会被真正回收。

  4. 对象的finalize()方法:如果对象在被标记为垃圾之前定义了finalize()方法,JVM会给予对象最后一次“自救”的机会。在下一次垃圾回收时,JVM会尝试调用这个对象的finalize()方法。如果对象在finalize()方法中重新与GC Roots建立了连接,它将被移出即将回收的列表。

  5. 分代收集:JVM通常采用分代收集算法,将对象分为新生代和老年代。新生代中的对象通常存活时间较短,而老年代中的对象存活时间较长。JVM会根据对象的年龄来决定它们应该在哪个区域。

垃圾回收是一个复杂的过程,JVM会根据当前的内存使用情况和对象的生命周期来决定何时以及如何回收对象。

JVM回收对象示例

局部变量作为GC Roots

public class App {public static void main(String[] args) {runApp(args);System.out.println("====");}private static void runApp(String[] args) {SpringApplication sApp = new SpringApplication();sApp.run(App.class, args);
//        sApp.getRunListeners(args);}
}public class SpringApplication {public String run(Class appClass, String[] args) {System.out.println("my Spring Application ");return "run args";}public String getRunListeners( String[] args) {System.out.println("My  getRunListeners ");String myRunListenersVar = "myRunListenersVar";this.getSpringFactoriesInstances(args);return "run args";}public String getSpringFactoriesInstances( String[] args) {System.out.println("My  getSpringFactoriesInstances ");String mySpringFactoriesInstancesVar = "mySpringFactoriesInstances";return "run args";}}

上述代码中,最关键的一段是 SpringApplication sApp = new SpringApplication();代码分析如下图所示:

JVM垃圾回收线程扫描到Spring Appliedcation发现它有一个GC Roots,此时就不能回收了。

静态变量作为GC Roots

除了上述代码外,咱们在看一个常见的代码。

public class App {public static SpringApplication sApp = new SpringApplication();public static void main(String[] args) {runApp(args);System.out.println("====");}private static void runApp(String[] args) {SpringApplication sApp = new SpringApplication();sApp.run(App.class, args);}
}

上述 public static SpringApplication sApp = new SpringApplication() 代码分析如下图所示:

JVM垃圾回收线程扫描到Spring Appliedcation发现它有一个GC Roots,此时就不能回收了。相信有细心的小伙伴注意到这块代码与上述代码在示意图上的区别了,他们GC Roots 所在区域不一样,一个是在JVM栈中,一个是在JVM的方法区且特意把颜色换成同堆内存一样的颜色,其实就是想说这块区域是共享内存,而局部变量是线程内存。

因此这里留一个小小的思考问题:大家直观感受下这两个引用谁引用持有的时间会更长一点?

简单总结,只要对象被局部变量或静态变量引用就不会被回收。

Java中不同对象类型

我们在进行可达性分析的时候常常提到局部变量持有某个对象的引用,或者静态变量持有某个对象引用,但是我们脑海里需要清楚一个概率那就是java中有不同的引用类型,他们分别是强引用、软引用、弱引用和虚引用。

强引用

强引用是最常见的引用类型,它使得对象在任何情况下都不会被垃圾回收,除非显式地将引用设置为null或超出作用域。如下代码所示:

public class App {public static SpringApplication app = new SpringApplication();
}

软引用

软引用提供了一种在内存不足时可以被回收的机制。它们通常用于缓存,当内存足够时,对象不会被回收,但如果内存不足,垃圾回收器会尝试回收这些对象。使用软引用可以提高应用的内存效率,但需要权衡对象被回收的概率.

import java.lang.ref.SoftReference;
// ...public class ConfigurationPropertyUtils {// ...private static final SoftReference<ConfigurationPropertyCache> cache = new SoftReference<>(null);public static <T> T getCached(BeanFactory beanFactory, Class<T> cacheType) {// ...SoftReference<ConfigurationPropertyCache> ref = cache;ConfigurationPropertyCache cpCache = (ref != null ? ref.get() : null);if (cpCache == null) {cpCache = new ConfigurationPropertyCache(beanFactory, cacheType);cache = new SoftReference<>(cpCache);}// ...return cpCache.getProperty();}// ...
}

在这段代码中,ConfigurationPropertyUtils类使用了一个静态的软引用cache来存储配置属性的缓存。当调用getCached方法时,如果缓存不存在或已被垃圾回收,就会创建一个新的ConfigurationPropertyCache实例,并用软引用包装后存储起来。由于软引用对象在内存不足时会被回收,这有助于减少垃圾回收器的压力,避免长时间的GC停顿,提高应用的响应性。

所以在软引用放到缓存这个场景就非常实用,想想内存充足我就不回收,内存不够了我回收而缓存基本上都是为了提升性能的,现在内存都不足了还有啥自行车,能运行就不错啦 !

弱引用

弱引用不会阻止垃圾回收器回收对象,即使内存充足也会被回收。它们通常用于监听或观察对象的状态,而不阻止对象的回收。弱引用在内存不足时更有可能被回收,因为它们对对象的生命周期没有任何控制。

public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupportimplements AsyncListenableTaskExecutor, SchedulingTaskExecutor {private final Object poolSizeMonitor = new Object();private int corePoolSize = 1;private int maxPoolSize = Integer.MAX_VALUE;private int keepAliveSeconds = 60;private int queueCapacity = Integer.MAX_VALUE;private boolean allowCoreThreadTimeOut = false;@Nullableprivate TaskDecorator taskDecorator;@Nullableprivate ThreadPoolExecutor threadPoolExecutor;// Runnable decorator to user-level FutureTask, if different
// 聚焦这里哈private final Map<Runnable, Object> decoratedTaskMap =new ConcurrentReferenceHashMap<>(16, ConcurrentReferenceHashMap.ReferenceType.WEAK);}

上述代码是Spring的ThreadPoolTaskExecutor节选部分,注意ThreadPoolTaskExecutor本身并不直接使用弱引用来引用任务对象。它主要使用弱引用来缓存线程池中的Future任务,以便在内存不足时可以回收这些不再需要的Future对象。这样做可以避免因缓存大量已完成或取消的任务的Future引用而导致的内存泄漏问题。

        一个对象被线程池持有强引用,那么即使这个对象不再被其他地方使用,它也不会被垃圾回收器回收,因为线程池中的引用阻止了垃圾回收。使用弱引用可以确保当没有其他强引用存在时,线程池中的任务可以被垃圾回收。

虚引用

虚引用是最弱的引用类型,它们几乎不提供任何保护,对象可以随时被回收。虚引用的主要作用是跟踪对象被回收的状态,通过ReferenceQueue来接收通知。

因为虚引用接触比较少,暂时就不讨论了,下面对他们四个应用简单总结下:

  • 强引用:只要强引用存在,对象就不会被垃圾回收。
  • 软引用:内存不足时,软引用对象会被垃圾回收,适用于缓存等场景。
  • 弱引用:弱引用对象在下一次垃圾回收时会被回收,适合用于监听或观察对象状态。
  • 虚引用:虚引用几乎不阻止对象回收,主要用于跟踪对象被垃圾回收的状态

到这里,我们已经Java介绍完垃圾对象的判断方法,并举出例子演示给大家看;同样提出几个问题:我们在介绍完GC Roots的时候分为不同类型的时候提出问题,他们引用时间的长短对于引用时间长短不同可以等价不同的对象实例存活时间。不同对象回收的机制一样嘛?如果不一样根据什么特点区分呢?回收以后内存空间要怎么整理呢?本章结束:有时间小伙伴可以回头看看,前面的文章回忆下各个区域的特点和整个分配的过程。从下一章我们将开始慢慢介绍GC算法,需要前面的铺垫。

 专题汇总

JVM专题一:深入分析Java工作机制

JVM专题二:Java如何进行编译的

JVM专题三:Java代码如何运行

JVM专题四:JVM的类加载机制

JVM专题五:类加载器与双亲委派机制

JVM专题六:JVM的内存模型

JVM专题七:JVM垃圾回收机制

JVM专题八:JVM如何判断可回收对象

JVM专题九:JVM分代知识点梳理

JVM专题十:JVM中的垃圾回收机制

JVM专题十一:JVM 中的收集器一

JVM专题十二:JVM 中的收集器二

JVM专题十三:总结与整理(面试常用)

相关文章:

JVM专题八:JVM如何判断可回收对象

在JVM专题七&#xff1a;JVM垃圾回收机制中提到JVM的垃圾回收机制是一个自动化的后台进程&#xff0c;它通过周期性地检查和回收不可达的对象&#xff08;垃圾&#xff09;&#xff0c;帮助管理内存资源&#xff0c;确保应用程序的高效运行。今天就让我们来看看JVM到底是怎么定…...

binary_cross_entropy_with_logits函数的参数设定

binary_cross_entropy_with_logits 该函数参数&#xff1a; logits (Tensor) - 输入预测值。其数据类型为float16或float32。 label (Tensor) - 输入目标值&#xff0c;shape与 logits 相同。数据类型为float16或float32。 weight (Tensor&#xff0c;可选) - 指定每个批次二…...

Python 面试【★★★★★】

欢迎莅临我的博客 &#x1f49d;&#x1f49d;&#x1f49d;&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

C# StringBuilder

以下是一些基本的 StringBuilder 使用方法&#xff1a;创建 StringBuilder 实例&#xff1a;追加字符串&#xff1a;插入字符串&#xff1a;删除字符串&#xff1a;替换字符串&#xff1a;清空 StringBuilder&#xff1a;转换 StringBuilder 为字符串&#xff1a;使用容量&…...

4个文章生成器免费版分享,让文章创作更轻松便捷

在当今这个信息飞速传播的时代&#xff0c;文章创作的重要性愈发凸显。无论是从事内容创作的专业人士&#xff0c;还是偶尔需要撰写文章的普通大众&#xff0c;都希望能更高效地完成文章创作任务。而在实际操作中&#xff0c;我们常常会遇到思路卡顿、没有创作灵感的问题。今天…...

redis-cluster(集群模式搭建)

redis中间件版本: redis-5.0.5环境介绍 这里使用服务器数量3&#xff0c;分别为172.0.0.1&#xff0c;172.0.0.2&#xff0c;172.0.0.3&#xff0c;每台机器redis节点数量2个&#xff0c;共6个redis节点构成redis-cluster模式。编译安装包 在172.0.0.1的机器上进入安装目录 cd …...

使用vite官网和vue3官网分别都可以创建vue3项目

问: npm init vitelatest 和 npm create vuelatest创建的vue3项目有什么区别? 回答: npm init vitelatest 和 npm create vuelatest 分别是使用 Vite 和 Vue CLI 工具创建 Vue 项目的两种方式&#xff0c;它们之间有几个主要区别&#xff1a; 1. **构建工具&#xff1a;** …...

PDF处理篇:如何调整 PDF 图像的大小

将视觉效果无缝集成到 PDF 中的能力使它们成为强大的通信工具。然而&#xff0c;笨拙的图像大小会迅速扰乱文档的流程&#xff0c;阻碍清晰度和专业性。幸运的是&#xff0c;GeekerPDF 和Adobe Acrobat等流行的应用程序提供了用户友好的解决方案来应对这一挑战。这个全面的指南…...

STM32 HAL库里 串口中断回调函数是在怎么被调用的?

跟着正点原子学习的HAL库写串口接收程序的时候一直有困惑&#xff0c;使用HAL_UART_Receive_IT开启接收中断后&#xff0c;为啥处理函数要写在HAL_UART_RxCpltCallback里&#xff0c;中断发生的时候是怎么到这个回调函数里去的&#xff1f; void MX_USART1_UART_Init(void) {h…...

音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析

音视频入门基础&#xff1a;H.264专题系列文章&#xff1a; 音视频入门基础&#xff1a;H.264专题&#xff08;1&#xff09;——H.264官方文档下载 音视频入门基础&#xff1a;H.264专题&#xff08;2&#xff09;——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…...

RT-Thread ENV-Windows v2.0.0安装教程

前言 前几天RT-Thread官方更新了env工具&#xff0c;开源仓库的Kconfig的写法都不大一样了&#xff1b;如果继续用原来的env工具&#xff0c;拉新代码之后很多示例都编译不了 在最新的env工具中menuconfig全面采用kconfiglib&#xff0c;升级env脚本和python版本&#xff0c;改…...

[HBM] HBM TSV (Through Silicon Via) 结构与工艺

依公知及经验整理&#xff0c;原创保护&#xff0c;禁止转载。 专栏 《深入理解DDR》 全文 3300 字。 1 概念 1.1 什么是HBM TSV 使用 TSV 堆叠多个DDR DRAM成为一块HBM, 成倍提高了存储器位宽&#xff0c; 一条位宽相当于高速公路的一条车道&#xff0c; 车道越多&#xff…...

基于STM32的温湿度检测TFT屏幕proteus恒温控制仿真系统

一、引言 本文介绍了一个基于STM32的恒温控制箱检测系统&#xff0c;该系统通过DHT11温湿度传感器采集环境中的温湿度数据&#xff0c;并利用TFT LCD屏幕进行实时显示。通过按键切换页面显示&#xff0c;通过按键切换实现恒温控制箱的恒温控制。为了验证系统的可靠性和稳定性&…...

【Qt+opencv】编译、配置opencv

文章目录 前言下载opencv编译opencvmingw版本 总结 前言 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库&#xff0c;它包含了超过2500个优化的算法。这些算法可以用来检测和识别面部&#xff0c;识别对象&#x…...

RDMA建链的3次握手和断链的4次挥手流程?

文章目录 基础信息建链 3次握手断链4次挥手建联状态active端passive端 报文结构函数关系其他后记 基础信息 CM: Communication Management 通信管理 连接管理SIDR: Service ID Resolution Protocol. 作用&#xff1a; enables users of Unreliable Datagram service to locate …...

实验4 图像空间滤波

1. 实验目的 ①掌握图像空间滤波的主要原理与方法&#xff1b; ②掌握图像边缘提取的主要原理和方法&#xff1b; ③了解空间滤波在图像处理和机器学习中的应用。 2. 实验内容 ①调用 Matlab / Python OpenCV中的函数&#xff0c;实现均值滤波、高斯滤波、中值滤波等。 ②调…...

独辟蹊径:我是如何用Java自创一套工作流引擎的(下)

作者&#xff1a;后端小肥肠 创作不易&#xff0c;未经允许严禁转载。 姊妹篇&#xff1a;独辟蹊径&#xff1a;我是如何用Java自创一套工作流引擎的&#xff08;上&#xff09;_java工作流引擎-CSDN博客 1. 前言 在上一篇博客中&#xff0c;我们详细介绍了如何利用Java语言从…...

【Python】pycharm常用快捷键操作

目录 一.pycharm自定义修改快捷键 二.pycharm默认常用快捷键 一.pycharm自定义修改快捷键 在file-setting-keymap中可以修改快捷键&#xff0c;建议刚开始没特殊需求就不用修改&#xff0c;先熟悉系统默认的常用快捷键&#xff0c;但是以下情况可以考虑修改: 之前使用其他I…...

es6语法复习一

es6语法 1.var 变量提升 2.let 不存在变量提升&#xff0c;只能定义一次 3.const 先定义再使用&#xff0c;定义好来不能修改 4.解构赋值 [a,b,c][1,2,3],{a,b,c}{a:1,b:2,c:3} 5.模版字符串 let aaa; ${a} is ok 6.对象简化写法 const school{ name, change, improve(){ cons…...

【python入门】自定义函数

文章目录 定义自定义函数的基本语法参数类型示例代码函数作用域匿名函数&#xff08;Lambda&#xff09;闭包装饰器 Python中的自定义函数允许你编写一段可重用的代码块&#xff0c;这段代码可以带参数&#xff08;输入&#xff09;&#xff0c;并可能返回一个值&#xff08;输…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码&#xff1a; https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...