Spring 单例bean 不被 GC 的真正原因

原因分两种情况:

  • 传统项目
  • SpringBoot项目

传统项目

传统的项目一般分spring的ioc容器和spring mvc的ioc容器,即父子容器。web容器应用上下文ServletContext会把spring ioc容器设置到其属性之中,这样两个容器都不会被gc,如下图(这里用的是springboot的,重点明白这个设置关系):

只要tomcat不关闭,ioc容器就存在,当然这里得保证ServletContext不会被gc,我们都知道ServletContext的生命周期跟tomcat的生命周期一致,具体是如何实现的呢,这个得看源码,这里就不分析了(原理我猜跟springboot的方式的类似的),有兴趣的同学可以翻翻源码。

SpringBoot项目

具体参考这个博主的文章:Spring bean 不被 GC 的真正原因。不赘述了, 稍微解释下,文章中的钩子函数的注册时机如下(传统项目没这个步骤):

为啥静态属性作为GC root不会被回收呢?

由JVM自带的类加载器所加载的类,在JVM的生命周期中,始终不会被卸载。JVM本身会始终引用这些类加载器,而这些类加载器始终引用它们所加载的类的Class对象。所以说,这些Class对象始终是可触及的。而由用户自定义的类加载器所加载的类是可以被卸载的。

上述钩子函数是在Runtime这个类中关联了ioc容器,rt.jar中,是由根类加载器加载的,只要根类加载器在,它就不会被卸载。而类一旦加载,其静态属性就存在方法区中,回收它只有通过类卸载。

总结

传统项目是通过把ioc容器设置到tomcat的应用上下文来保证不会被gc的,而springBoot项目是通过注册一个钩子函数,使IOC容器和根类加载器加载的类Runtime的静态属性关联从而不被gc。

扫描二维码关注公众号,回复: 17496812 查看本文章

另外springboot项目也会像传统项目那样把ioc容器设置到tomcat应用上下文,为啥这个不能保证不被gc呢?这是因为内嵌tomcat是ioc容器的一个属性,它是随ioc生而生,随ioc死而死的。如下:

另外多例bean是会被回收的,因为它不像单例那样会用一个Map保存起来,而是用的时候新建。

相关文章:

Java中类的卸载机制

猜你喜欢

转载自blog.csdn.net/meser88/article/details/139033441