JVM专题八:JVM如何判断可回收对象
在JVM专题七:JVM垃圾回收机制中提到JVM的垃圾回收机制是一个自动化的后台进程,它通过周期性地检查和回收不可达的对象(垃圾),帮助管理内存资源,确保应用程序的高效运行。今天就让我们来看看JVM到底是怎么定义“垃圾”对象的。
JVM垃圾对象判断方法
JVM在垃圾回收(Garbage Collection简称GC)过程中定义“垃圾”对象,主要是根据对象是否还可达来判断。以下是一些判断对象是否成为垃圾的常见标准:
-
引用计数法:这是一种简单的垃圾回收方法,每个对象都有一个引用计数器,每当有引用指向该对象时,计数器加一;每当引用离开作用域或被赋值为null时,计数器减一。当引用计数器为零时,对象被认为是垃圾。但这种方法无法处理循环引用的问题。
-
可达性分析:这是现代JVM中常用的垃圾回收方法。从一系列的“GC Roots”开始,所有能够通过引用链到达的对象都被认为是存活的,而那些无法到达的对象则被认为是垃圾。GC Roots通常包括:
- 虚拟机栈(栈帧中的本地变量表)中的引用对象
- 方法区中的类静态属性引用的对象
- 运行时常量池中引用的对象
- JNI(Java Native Interface)的引用对象
-
不可达的对象:即使对象不可达,JVM也不会立即回收它们。JVM会将这些对象标记为“即将回收”的状态。在下一次垃圾回收周期中,这些对象会被真正回收。
-
对象的finalize()方法:如果对象在被标记为垃圾之前定义了
finalize()
方法,JVM会给予对象最后一次“自救”的机会。在下一次垃圾回收时,JVM会尝试调用这个对象的finalize()
方法。如果对象在finalize()
方法中重新与GC Roots建立了连接,它将被移出即将回收的列表。 -
分代收集: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专题七:JVM垃圾回收机制中提到JVM的垃圾回收机制是一个自动化的后台进程,它通过周期性地检查和回收不可达的对象(垃圾),帮助管理内存资源,确保应用程序的高效运行。今天就让我们来看看JVM到底是怎么定…...
binary_cross_entropy_with_logits函数的参数设定
binary_cross_entropy_with_logits 该函数参数: logits (Tensor) - 输入预测值。其数据类型为float16或float32。 label (Tensor) - 输入目标值,shape与 logits 相同。数据类型为float16或float32。 weight (Tensor,可选) - 指定每个批次二…...

Python 面试【★★★★★】
欢迎莅临我的博客 💝💝💝,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...
C# StringBuilder
以下是一些基本的 StringBuilder 使用方法:创建 StringBuilder 实例:追加字符串:插入字符串:删除字符串:替换字符串:清空 StringBuilder:转换 StringBuilder 为字符串:使用容量&…...

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

redis-cluster(集群模式搭建)
redis中间件版本: redis-5.0.5环境介绍 这里使用服务器数量3,分别为172.0.0.1,172.0.0.2,172.0.0.3,每台机器redis节点数量2个,共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 项目的两种方式,它们之间有几个主要区别: 1. **构建工具:** …...

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

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

音视频入门基础:H.264专题(5)——FFmpeg源码中 解析NALU Header的函数分析
音视频入门基础:H.264专题系列文章: 音视频入门基础:H.264专题(1)——H.264官方文档下载 音视频入门基础:H.264专题(2)——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…...

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

[HBM] HBM TSV (Through Silicon Via) 结构与工艺
依公知及经验整理,原创保护,禁止转载。 专栏 《深入理解DDR》 全文 3300 字。 1 概念 1.1 什么是HBM TSV 使用 TSV 堆叠多个DDR DRAM成为一块HBM, 成倍提高了存储器位宽, 一条位宽相当于高速公路的一条车道, 车道越多ÿ…...

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

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

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

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

独辟蹊径:我是如何用Java自创一套工作流引擎的(下)
作者:后端小肥肠 创作不易,未经允许严禁转载。 姊妹篇:独辟蹊径:我是如何用Java自创一套工作流引擎的(上)_java工作流引擎-CSDN博客 1. 前言 在上一篇博客中,我们详细介绍了如何利用Java语言从…...
【Python】pycharm常用快捷键操作
目录 一.pycharm自定义修改快捷键 二.pycharm默认常用快捷键 一.pycharm自定义修改快捷键 在file-setting-keymap中可以修改快捷键,建议刚开始没特殊需求就不用修改,先熟悉系统默认的常用快捷键,但是以下情况可以考虑修改: 之前使用其他I…...
es6语法复习一
es6语法 1.var 变量提升 2.let 不存在变量提升,只能定义一次 3.const 先定义再使用,定义好来不能修改 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入门】自定义函数
文章目录 定义自定义函数的基本语法参数类型示例代码函数作用域匿名函数(Lambda)闭包装饰器 Python中的自定义函数允许你编写一段可重用的代码块,这段代码可以带参数(输入),并可能返回一个值(输…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...