概述

自从开始接触 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

Spring bean 不被 GC 的真正原因相关推荐

  1. Spring Bean 中的线程安全

    在 使用Spring框架时,很多时候不知道或者忽视了多线程的问题.因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线 程测试的环境.但如果不去考虑潜在的漏洞,它就会变成 ...

  2. 【一步一步学习spring】spring bean管理(上)

    1. spring 工厂类 我们前边的demo中用到的spring 工厂类是ClassPathXmlApplicationContext,从上图可以看到他还有一个兄弟类FileSystemApplic ...

  3. Spring(3)——装配 Spring Bean 详解

    装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 Bean 装配到 Spring IoC 容器中. 大部分场景下,我们都会使用 Appl ...

  4. Spring8:一些常用的Spring Bean扩展接口

    前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...

  5. 带你读懂Spring Bean 的生命周期,嘿,就是玩儿~

    带你读懂Spring Bean 的生命周期,嘿,就是玩儿~ 一.前言 今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象.因为 Spring Bean 的 ...

  6. 【SpringBoot】查看运行环境中所有的spring bean

    前言 spring boot : 2.0.0.RELEASE maven eclipse 在开发&调试过程中,提示某个Bean找不到.此时就需要查看运行环境中有没有这个bean,以便快速排除出 ...

  7. 如何在没有Springockito的情况下模拟Spring bean

    我在Spring工作了几年. 但是我总是对XML配置变得多么混乱感到沮丧. 随着各种注释和Java配置可能性的出现,我开始喜欢使用Spring进行编程. 这就是为什么我强烈使用Java配置的原因. 我 ...

  8. Spring @Bean和PropertyPlaceHolderConfigurer

    最近,我被我认为将是一个相当简单的实现所困扰-考虑以下基于Spring Java的bean定义文件( @Configuration ): package root;...@Configuration ...

  9. Spring框架:三种Spring Bean生命周期技术

    当使用术语"生命周期"时,Spring的家伙指的是您的bean的构造和破坏,通常这与Spring Context的构造和破坏有关. 在某些情况下,Bean生命周期的管理不是一件容易 ...

最新文章

  1. yum安装软件包提示Error Downloading Packages解决方法
  2. 烂泥:centos单独编译安装gd库
  3. ……OleContainer中嵌入WORD后,用什么办法显示滚动条???……
  4. Python max 函数 - Python零基础入门教程
  5. MFC开发IM-第六篇、MFC 文本框 用法
  6. hdu 1312深搜入门题
  7. C++VS2019中新建自定义模板与删除自定义模板
  8. [未完成]scikit-learn一般实例之九:用于随机投影嵌入的Johnson–Lindenstrauss lemma边界...
  9. Fedora Remix for WSL 登陆微软商城
  10. premiere(Pr)制作逐字出现效果
  11. lintcode 丢鸡蛋
  12. iometer - 性能压力测试工具
  13. EXCEL startup.exe 宏病毒
  14. 在linux下成功写Bingo游戏
  15. 担忧:房价八大预言,市场来检验。
  16. 波特率(baud)与比特率(bit/s)的差别
  17. php过滤微信表情符号的正则表达式方法
  18. 数字图像处理及其表示
  19. 20秋PHP作业1,南开17秋学期(清考)《电子商务理论与实践》在线作业1
  20. 什么是DataOps?难道DataOps只是面向Data 的Ops吗?

热门文章

  1. Haproxy实现负载均衡
  2. python struct pack解析_Python struct 详解
  3. ruby推送示例_Ruby直到示例循环
  4. linux cnc_CNC的完整形式是什么?
  5. 阿里巴巴Java开发手册建议设置HashMap的初始容量,但设置多少合适呢?
  6. 三层业务类(DAL)必用的通用方法之一
  7. Android JSON数据与实体类之间的相互转化-------GSON的简单用法
  8. Qt QtCreator 所有版本官方下载地址
  9. vbs代码炫酷效果_Python|实现黑客帝国代码雨效果
  10. mysql 字符集 校验规则_MySQL字符集及校验规则