Spring bean 不被 GC 的真正原因
概述
自从开始接触 Spring
之后,一直以来都在思考一个问题,在 Spring
应用的运行过程中,为什么这些 bean
不会被回收?
今天深入探究了这个问题之后,才有了答案。
思考点
大家都知道,一个 bean
会不会被回收,取决于对象存活判定算法。在 JVM
底层中使用的是可达性分析算法,抛开 HotSpot
的实现细节不谈,那么一个对象被判定为死亡,应该与 GC Root
不存在可达的引用路径。
所以,Spring 的 bean 肯定是与 GC Root 存在可达的引用路径,才不会被回收掉
在 Java
语言对于 GC Root
的定义中,以下几种对象可以作为 GC Root
:
- 虚拟机栈的栈帧中的本地变量表中,引用类型对象所指向的堆中的对象
- 处于运行中状态(
RUNNABLE
,BLOCKED
,WAITING
,TIMED_WAITING
)的线程对象 JDK
自带的类加载器对象- 本地方法所引用的对象
JVM
持有的对象,例如基本类型的Class
对象,NullPointerException
等常用异常对象- 被
synchronized
关键字修饰的对象
一般来说,只要是符合上面这几种规则的对象,或者能由上面的规则推导出存在引用的对象,都可以作为 GC Root
。
那么 Spring
的 bean
的 GC Root
是哪一种呢?或者说,找到了 Spring
的 bean
的 GC Root
,就找到了问题的答案。
动手寻找答案
首先新建一个 SpringBoot
应用,里面定义了两个 bean
以及一个启动类,包结构如下:
然后点击运行启动类,启动完成之后,打开 jvisualVM
,找到对应的应用,然后点击生成当前堆 dump
:
然后打开后选择类,输入 Hello
过滤类名,找到 HelloWorldService
,点击在实例视图中显示,发现只有一个实例存在,这符合我们的预期。
最后右键点击这个实例,选择显示最近的垃圾回收根节点,可以观察到如下的引用路径:
可以看到,DefaultListableBeanFactory
和 AnnotationConfigServletWebServerApplicationContext
都是我们比较熟悉的 bean
容器,对应的往下找发现有 ConcurrentHashMap$Node
引用。我们都知道在 Spring
中,正是这两个容器(准确地说是 DefaultListableBeanFactory
)中使用 ConcurrentHashMap
存放了实例化好的 bean
。 这都是非常符合我们预期的。
但是在 AbstractApplicationContext
再往上找后,发现有个叫 ApplicationShutdownHooks
的东西。意思就是说,我们的容器,最终与这个 ApplicationShutdownHooks
的东西扯上了引用关系。接着我们翻阅 Spring
源码进行求证:
发现在 AbstractApplicationContext
的 registerShutdownHook
方法中调用了这一行代码,而 registerShutdownHook
方法正是在 Spring
容器初始化时要调用的方法:
这说明在 Spring
容器初始化时,调用的这个方法,然后在继续往里跟踪这个方法:
最后我们可以发现,AbstractApplicationContext
中的 Thread shutdownHook
变量,最终被放在了 ApplicationShutdownHooks
的这个 map
里面,而这个 map
恰好就是一个静态变量。
结论
所以,Spring
的 bean
没有被回收,正是因为在 AbstractApplicatuonContext
的 registerShutdownHook
方法中,与 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 的真正原因相关推荐
- Spring Bean 中的线程安全
在 使用Spring框架时,很多时候不知道或者忽视了多线程的问题.因为写程序时,或做单元测试时,很难有机会碰到多线程的问题,因为没有那么容易模拟多线 程测试的环境.但如果不去考虑潜在的漏洞,它就会变成 ...
- 【一步一步学习spring】spring bean管理(上)
1. spring 工厂类 我们前边的demo中用到的spring 工厂类是ClassPathXmlApplicationContext,从上图可以看到他还有一个兄弟类FileSystemApplic ...
- Spring(3)——装配 Spring Bean 详解
装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 Bean 装配到 Spring IoC 容器中. 大部分场景下,我们都会使用 Appl ...
- Spring8:一些常用的Spring Bean扩展接口
前言 Spring是一款非常强大的框架,可以说是几乎所有的企业级Java项目使用了Spring,而Bean又是Spring框架的核心. Spring框架运用了非常多的设计模式,从整体上看,它的设计严格 ...
- 带你读懂Spring Bean 的生命周期,嘿,就是玩儿~
带你读懂Spring Bean 的生命周期,嘿,就是玩儿~ 一.前言 今天我们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象.因为 Spring Bean 的 ...
- 【SpringBoot】查看运行环境中所有的spring bean
前言 spring boot : 2.0.0.RELEASE maven eclipse 在开发&调试过程中,提示某个Bean找不到.此时就需要查看运行环境中有没有这个bean,以便快速排除出 ...
- 如何在没有Springockito的情况下模拟Spring bean
我在Spring工作了几年. 但是我总是对XML配置变得多么混乱感到沮丧. 随着各种注释和Java配置可能性的出现,我开始喜欢使用Spring进行编程. 这就是为什么我强烈使用Java配置的原因. 我 ...
- Spring @Bean和PropertyPlaceHolderConfigurer
最近,我被我认为将是一个相当简单的实现所困扰-考虑以下基于Spring Java的bean定义文件( @Configuration ): package root;...@Configuration ...
- Spring框架:三种Spring Bean生命周期技术
当使用术语"生命周期"时,Spring的家伙指的是您的bean的构造和破坏,通常这与Spring Context的构造和破坏有关. 在某些情况下,Bean生命周期的管理不是一件容易 ...
最新文章
- yum安装软件包提示Error Downloading Packages解决方法
- 烂泥:centos单独编译安装gd库
- ……OleContainer中嵌入WORD后,用什么办法显示滚动条???……
- Python max 函数 - Python零基础入门教程
- MFC开发IM-第六篇、MFC 文本框 用法
- hdu 1312深搜入门题
- C++VS2019中新建自定义模板与删除自定义模板
- [未完成]scikit-learn一般实例之九:用于随机投影嵌入的Johnson–Lindenstrauss lemma边界...
- Fedora Remix for WSL 登陆微软商城
- premiere(Pr)制作逐字出现效果
- lintcode 丢鸡蛋
- iometer - 性能压力测试工具
- EXCEL startup.exe 宏病毒
- 在linux下成功写Bingo游戏
- 担忧:房价八大预言,市场来检验。
- 波特率(baud)与比特率(bit/s)的差别
- php过滤微信表情符号的正则表达式方法
- 数字图像处理及其表示
- 20秋PHP作业1,南开17秋学期(清考)《电子商务理论与实践》在线作业1
- 什么是DataOps?难道DataOps只是面向Data 的Ops吗?
热门文章
- Haproxy实现负载均衡
- python struct pack解析_Python struct 详解
- ruby推送示例_Ruby直到示例循环
- linux cnc_CNC的完整形式是什么?
- 阿里巴巴Java开发手册建议设置HashMap的初始容量,但设置多少合适呢?
- 三层业务类(DAL)必用的通用方法之一
- Android JSON数据与实体类之间的相互转化-------GSON的简单用法
- Qt QtCreator 所有版本官方下载地址
- vbs代码炫酷效果_Python|实现黑客帝国代码雨效果
- mysql 字符集 校验规则_MySQL字符集及校验规则