原因分两种情况:
- 传统项目
- 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。

另外springboot项目也会像传统项目那样把ioc容器设置到tomcat应用上下文,为啥这个不能保证不被gc呢?这是因为内嵌tomcat是ioc容器的一个属性,它是随ioc生而生,随ioc死而死的。如下:
另外多例bean是会被回收的,因为它不像单例那样会用一个Map保存起来,而是用的时候新建。
相关文章: