Spring bean 不被 GC 的真正原因

概述

自从开始接触 Spring 之后,一直以来都在思考一个问题,在 Spring 应用的运行过程中,为什么这些 bean 不会被回收?
今天深入探究了这个问题之后,才有了答案。

思考点

大家都知道,一个 bean 会不会被回收,取决于对象存活判定算法。在 JVM 底层中使用的是可达性分析算法,抛开 HotSpot 的实现细节不谈,那么一个对象被判定为死亡,应该与 GC Root 不存在可达的引用路径。

所以,Spring 的 bean 肯定是与 GC Root 存在可达的引用路径,才不会被回收掉

Java 语言对于 GC Root 的定义中,以下几种对象可以作为 GC Root

  • 虚拟机栈的栈帧中的本地变量表中,引用类型对象所指向的堆中的对象
  • 处于运行中状态(RUNNABLEBLOCKEDWAITINGTIMED_WAITING)的线程对象
  • JDK 自带的类加载器对象
  • 本地方法所引用的对象
  • JVM 持有的对象,例如基本类型的 Class 对象,NullPointerException 等常用异常对象
  • synchronized 关键字修饰的对象

一般来说,只要是符合上面这几种规则的对象,或者能由上面的规则推导出存在引用的对象,都可以作为 GC Root
那么 SpringbeanGC Root 是哪一种呢?或者说,找到了 SpringbeanGC Root,就找到了问题的答案。

动手寻找答案

首先新建一个 SpringBoot 应用,里面定义了两个 bean 以及一个启动类,包结构如下:
在这里插入图片描述

然后点击运行启动类,启动完成之后,打开 jvisualVM ,找到对应的应用,然后点击生成当前dump
在这里插入图片描述

然后打开后选择类,输入 Hello 过滤类名,找到 HelloWorldService,点击在实例视图中显示,发现只有一个实例存在,这符合我们的预期。
最后右键点击这个实例,选择显示最近的垃圾回收根节点,可以观察到如下的引用路径:
在这里插入图片描述
可以看到,DefaultListableBeanFactoryAnnotationConfigServletWebServerApplicationContext 都是我们比较熟悉的 bean 容器,对应的往下找发现有 ConcurrentHashMap$Node 引用。我们都知道在 Spring 中,正是这两个容器(准确地说是 DefaultListableBeanFactory)中使用 ConcurrentHashMap 存放了实例化好的 bean。 这都是非常符合我们预期的。
但是在 AbstractApplicationContext 再往上找后,发现有个叫 ApplicationShutdownHooks 的东西。意思就是说,我们的容器,最终与这个 ApplicationShutdownHooks 的东西扯上了引用关系。接着我们翻阅 Spring 源码进行求证:
在这里插入图片描述

发现在 AbstractApplicationContextregisterShutdownHook 方法中调用了这一行代码,而 registerShutdownHook 方法正是在 Spring 容器初始化时要调用的方法:
在这里插入图片描述

这说明在 Spring 容器初始化时,调用的这个方法,然后在继续往里跟踪这个方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最后我们可以发现,AbstractApplicationContext 中的 Thread shutdownHook 变量,最终被放在了 ApplicationShutdownHooks 的这个 map 里面,而这个 map 恰好就是一个静态变量。

结论

所以,Springbean 没有被回收,正是因为在 AbstractApplicatuonContextregisterShutdownHook 方法中,与 ApplicationShutdownHooks 中的一个静态变量建立了可达的引用路径。

题外话

那么为什么类的静态变量可以作为 GC Root 呢?抱着严谨的心态,我们继续往下求证:

类的静态变量属于类对象,类对象由类加载器进行加载,而类加载器是 GC Root,那么类加载器是不是与被加载的类对象存在引用关系呢?

翻阅 ClassLoader 类,赫然看到这一段代码:

public abstract class ClassLoader {// The classes loaded by this class loader. The only purpose of this table// is to keep the classes from being GC'ed until the loader is GC'ed.private final Vector<Class<?>> classes = new Vector<>();
}

注释一目了然,好家伙!原来类加载器把所有的已加载的类对象都保存在这个容器里面,怪不得类对象和类静态变量也属于 GC Root

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

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

相关文章

ad域时间源配置_域控制器server2012时间同步NTP配置

一、域控配置1.修改注册表&#xff0c;设置域控服务器名称2.设置组策略&#xff0c;启动NTP服务器3.域策略中设置windows time服务自动启动二、服务端配置(Ntp服务器&#xff0c;客户端将根据这台服务器的时间进行同步)1、微软键R键&#xff0c;进入“运行”&#xff0c;输入“…

【java8新特性】——Optional详解(三)

一、简介 Optional类是Java8为了解决null值判断问题&#xff0c;借鉴google guava类库的Optional类而引入的一个同名Optional类&#xff0c;使用Optional类可以避免显式的null值判断&#xff08;null的防御性检查&#xff09;&#xff0c;避免null导致的NPE&#xff08;NullPo…

不使用 + 和 - 运算符计算两整数之和

问题概述 不使用运算符 和 -&#xff0c;计算两整数之和 思考 不使用 和 - &#xff0c;那就只能想到用位运算来处理了。思路如下&#xff1a; 两数进行 ^&#xff08;异或运算&#xff09;&#xff0c;可以得到两个数在相同位上数值不同的相加结果两数进行 &&#x…

vts传感器采取船舶的_详解虎门大桥监测系统:传感器与物联网功不可没

来源&#xff1a;传感器专家网近日&#xff0c;虎门大桥“虎躯一震”给全国人民来了个“深呼吸”。虎门大桥是广东沿海地区重要的交通枢纽&#xff0c;始建于1992年&#xff0c;1997年通车至今&#xff0c;大桥一直都十分平稳。但在5月5日下午&#xff0c;虎门大桥发生异常抖动…

宝塔安装sqlserver_linux宝塔面板安装安装 pdo_sqlsrv扩展

第一步安装源curl https://packages.microsoft.com/config/rhel/7/prod.repo > /etc/yum.repos.d/mssqlrelease.repo第二步安装驱动yum install msodbcsql mssql-tools unixODBC-devel第三步下载pdo-sqlsrv源码wget http://pecl.php.net/get/pdo_sqlsrv-5.6.1.tgztar -zxvf …

【java8新特性】——方法引用(四)

一、简介 方法引用是java8的新特性之一&#xff0c; 可以直接引用已有Java类或对象的方法或构造器。方法引用与lambda表达式结合使用&#xff0c;可以进一步简化代码。 来看一段简单代码&#xff1a; public static void main(String[] args) {List<String> strList Ar…

MySQL 排名函数.md

概述 MySQL 自带的排名的函数&#xff0c;主要有&#xff1a; row_number()rank()dense_rank()ntile() 测试数据 测试数据如下所示&#xff1a; row_number() 函数 用法如下&#xff1a; SELECT row_number() OVER (ORDER BY Salary DESC) row_num,Salary FROMEmployee查…

【java8新特性】——默认方法(五)

一、简介 默认方法是指接口的默认方法&#xff0c;它是java8的新特性之一。顾名思义&#xff0c;默认方法就是接口提供一个默认实现&#xff0c;且不强制实现类去覆写的方法。默认方法用default关键字来修饰。 默认方法可以解决的痛点&#xff1a; 在java8之前&#xff0c;修…

Java 序列化总结.md

概述 序列化&#xff1a;将对象写入到 IO 流中反序列化&#xff1a;从 IO 流中恢复对象 实现方法 实现 Serializable 或者 Externalizable Serializable&#xff1a;标记接口&#xff0c;不用实现任何方法&#xff0c;可以指定序列化 IDExternalizable&#xff1a;增强的序…

多线程买票案例

测试类 package thead;public class testThread {public static void main(String [] arg){Tickets ticket new Tickets();Thread t1 new Thread(ticket,"窗口一&#xff1a;");Thread t2 new Thread(ticket,"窗口二&#xff1a;");Thread t3 new Thr…

深度学习auc_机器学习集成学习与模型融合!

↑↑↑关注后"星标"Datawhale每日干货 & 每月组队学习&#xff0c;不错过Datawhale干货 作者&#xff1a;李祖贤&#xff0c;深圳大学&#xff0c;Datawhale高校群成员对比过kaggle比赛上面的top10的模型&#xff0c;除了深度学习以外的模型基本上都是集成学习的…

常用并发工具类(锁和线程间通信工具类)

常用并发工具类总结 JUC 下的常用并发工具类&#xff08;锁和线程间通信工具类&#xff09;&#xff0c;主要包括 ReentrantLock、ReentrantReadWriteLock、CountDownLatch、CyclicBarrier、Semaphore、Exchanger ReentrantLock 和 ReentrantReadWriteLock ReentrantLock 是…

of方法:给集合一次性添加多个元素

of()方法只是Map&#xff0c;List&#xff0c;Set这三个接口的静态方法&#xff0c;其父类接口和子类实现并没有这类方法&#xff0c;比如 HashSet&#xff0c;ArrayList返回的集合是不可变的&#xff0c;再次添加会报错Set与Map集合不可以存储重复的元素&#xff0c;否则会报错…

数控车椭圆编程实例带图_数控车床编程教程,图文实例详解

一、数控车编程特点(1) 可以采用绝对值编程(用X、Z表示)、增量值编程(用U、W表示)或者二者混合编程。(2) 直径方向(X方向) 系统默认为直径编程&#xff0c;也可以采用半径编程&#xff0c;但必须更改系统设定。(3) X向的脉冲当量应取Z向的一半。(4)采用固定循环&#xff0c;简化…

常用并发工具类(并发集合类)

文章目录概述BlockingQueueArrayBlockingQueue数据存储相关属性阻塞特性相关属性主要方法LinkedBlockingQueueLinkedBlockingQueue 主要属性LinkedBlockingQueue 设计思想ConcurrentLinkedQueuePriorityBlockingQueuePriorityBlockingQueue 主要属性PriorityBlockingQueue 设计…

参考文献起止页码怎么写_毕业论文文献综述不会写?快来看看这篇文章(附含通用模板)...

文献综述是对所研究主题的现状进行客观的叙述和评论、寻求新的研究突破点。一个资料全面、研究深入的综述不仅可以帮助作者确立毕业论文的选题&#xff0c;还可以为论文的深入研究提供有力的支撑。本文分享一份"毕业论文文献综述万能模板",以供参考。一、文献综述的基…

常用并发工具类(线程池)

文章目录概述ThreadPoolExecutorThreadPoolExecutor 的主要属性Worker 主要属性线程池的状态线程池的状态流转线程池提交任务的执行流程线程数量的设置线程池的种类FixedThreadPoolCachedThreadPoolSingleThreadExecutorScheduledThreadPoolExecutorSingleThreadScheduledExecu…

【Java 8 新特性】Java Stream.of()用法示例

本页将介绍Java Stream.of方法示例。Stream.of用于为给定元素创建顺序流。我们可以传递单个元素或多个元素。 查看javadoc中Stream.of方法声明。 static <T> Stream<T> of(T t) 参数&#xff1a;传递单个元素。 返回&#xff1a;该方法返回一个包含一个元素的流。…

Java 类加载机制

文章目录概述类的生命周期类加载的时机类加载的主要 5 个阶段加载验证准备准备阶段初始值的含义解析符号引用直接引用解析阶段的理解静态绑定与动态绑定初始化类加载器类加载器与类之间的关系类加载器的种类双亲委派机制双亲委派机制设计目的破坏双亲委派机制破坏双亲委派机制的…

Java –什么是瞬态字段?

在Java中&#xff0c; transient字段在序列化过程中被排除。 简而言之&#xff0c;当我们将对象保存到文件中&#xff08;序列化&#xff09;时&#xff0c;所有transient字段都将被忽略。 1. POJO 瞬态 复查以下Person类&#xff1b; 薪水领域是transient 。 public class …