详解Spring如何避免被JVM 垃圾回收

 更新时间:2023年11月16日 09:15:23   作者:五阳神功  
如果Spring 被回收掉,Spring管理的bean全部会被回收,那我们的Java应用不就被一锅端了吗?所以本文小编将和大家一起聊聊Spring如何避免被JVM垃圾回收,需要的朋友可以参考下

回顾一下,垃圾回收的基础知识

在Java堆中,存放着所有Java的对象实例。在进行垃圾收集之前,JVM需要确定哪些对象已经不被使用(即垃圾),哪些对象仍然被使用。为了判断对象是否是“垃圾”,JVM采用了可达性分析算法。

可达性分析算法 是指通过指定 GC Root 根对象,从根对象开始搜索引用的对象,通过引用链条,层层遍历链条上的对象,可以到达的对象不可被垃圾回收。而最终没有被搜索遍历到的对象,则为 不可达对象,应该被垃圾回收。

JVM中的 GC Root根对象包括如下:

  • 虚拟机栈引用的对象
  • 本地方法栈内JNI(本地方法)引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • Java虚拟机内部的引用

如果 Spring 想不被垃圾回收,那么Spring一定要确保自己被以上 GC Root引用,以上五个,任意一个即可。

接下来,我们将分析Spring 源码!找到Spring不被垃圾回收的奥秘!

启动Spring Boot应用

以下代码启动了一个Spring Boot应用,这是官方推荐的启动方式,通过注解的方式,把启动类传递给 SpringApplication ,调用run 方法,启动Spring Boot。

@RestController
@SpringBootApplication
public class MyApplication {

    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

需要说明的是,run方法在Spring boot启动成功后,会立即返回,不会被阻塞。所以 main 线程在启动Spring boot后,将退出……

由于Spring Boot会启动Jetty/Tomcat等其他线程池,所以Java应用并不会退出。因为Java进程退出条件之一是:所有非守护线程全部退出,则JVM退出,所以只有 main 线程退出,其他业务线程还存在情况下,Java不会退出。

由此可见,main 线程在启动 Spring Boot后,并不会一直持有 Spring boot 对象引用,官方文档里也没有 强调,一定要保持 main 线程 不退出。Spring Boot需要把自己交到其他对象手中,确保自己不被回收!

那么如何保证 Spring Boot不被垃圾回收呢?我需要从 SpringApplcaition 内部找原因!

Spring Boot 和 Tomcat

从上面的代码可以看到 Spring Boot控制了Java应用的入口,而Web容器 Tomcat等被Spring 管理,如果Spring不会被垃圾回收,那么Tomcat就不用担心被垃圾回收。

而在Spring Boot之前的Web应用,都是将Java项目打包到Tomcat容器中执行。那时候 Spring MVC和Spring 是要被Tomcat容器管理的,所以那时的Spring项目不用担心 被垃圾回收的问题。

而现在 Tomcat和 Spring boot的角色互换,决定了 Spring Boot应用必须要处理好垃圾回收问题!

探究Spring Boot代码

创建和启动上下文

下图是 SpringApplication.run方法的源代码,run 方法主要执行两步,创建 Spring 上下文和启动上下

刷新上下文的奥秘

在Spring Boot刷新上下文的代码中,首先调用 Spring Application.refresh方法启动Spring 上下文。然后 Spring boot就把 Close 方法注册到 Java shutdownHook 关闭钩子程序中!

基本上可以破案了!

因为Spring Boot控制了Java程序的入口,所以要负责整个项目的关闭流程,于是它 注册了Java关闭钩子。

接下来,我们看一下注册关闭钩子,会被 GC Root引用到吗?

关闭钩子

add 方法,将钩子程序注册到 一个容器中!

可以看到 Thread 类型的 钩子程序,被保存在 hooks Map 中。

而hooks列表的类型定义是 static 类型的。 static 变量都是GC Root。

总结

Spring Boot 在启动时会将关闭流程注册到 Java 关闭钩子中,并通过关闭钩子线程引用到 Spring 上下文。

关闭钩子会被保存在一个 static 静态类型的 Map 中,这个 Map 在 GC Root 上。

因此,Spring Boot 不被垃圾回收的关键是在启动时注册了关闭钩子。

破案了,spring永远不会被垃圾回收。

以上就是详解Spring如何避免被JVM 垃圾回收的详细内容,更多关于Spring避免被JVM垃圾回收的资料请关注脚本之家其它相关文章!

相关文章

  • spring中使用@Autowired注解无法注入的情况及解决

    spring中使用@Autowired注解无法注入的情况及解决

    这篇文章主要介绍了spring中使用@Autowired注解无法注入的情况及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • MyBatis中特殊符号的转义

    MyBatis中特殊符号的转义

    编写SQL中会用到<,>,,>= 等,但是在mybatis中不可以这么写,与xml文件的元素冲突,所以需要转义,本文主要介绍了MyBatis中特殊符号的转义,主要介绍了两种转义方式,感兴趣的可以了解一下
    2024-01-01
  • PowerJob的AbstractScriptProcessor实现类工作流程源码解读

    PowerJob的AbstractScriptProcessor实现类工作流程源码解读

    这篇文章主要为大家介绍了PowerJob的AbstractScriptProcessor源码流程解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Java编程中的性能优化如何实现

    Java编程中的性能优化如何实现

    这篇文章主要介绍了Java编程中的性能优化如何实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • spring IOC容器的Bean管理XML自动装配过程

    spring IOC容器的Bean管理XML自动装配过程

    这篇文章主要为大家介绍了spring IOC容器Bean管理基于XML的自动装配过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Java8 LocalDateTime时间日期类使用实例详解

    Java8 LocalDateTime时间日期类使用实例详解

    本文从 LocalDateTime 类的创建、转换、格式化与解析、计算与比较以及其他操作几个方面详细介绍了 LocalDateTime 类在 Java 8 中的使用,感兴趣的朋友跟随小编一起看看吧
    2024-03-03
  • 关于jvm内存如何查看

    关于jvm内存如何查看

    本文介绍了如何在Java虚拟机(JVM)中进行内存管理,包括查看JVM内存情况的常用方法和工具,这些方法包括使用JDK自带的工具如jps、jstat、jmap和jconsole,以及一些第三方监控工具如Prometheus、Grafana和ElasticStack等,通过这些方法
    2024-09-09
  • Java8中新判空方法之Optional类的使用详解

    Java8中新判空方法之Optional类的使用详解

    Opitonal类就是Java提供的为了解决大家平时判断对象是否为空用的。本文将通过示例为大家讲解一下Optional类的使用,感兴趣的可以收藏一下
    2022-12-12
  • Java遍历Map集合的方法(最新推荐)

    Java遍历Map集合的方法(最新推荐)

    这篇文章主要介绍了Java遍历Map集合的方法,遍历map的key集合然后通过key获取value,本文给大家讲解的非常详细,需要的朋友可以参考下
    2024-05-05
  • java多线程读取多个文件的方法

    java多线程读取多个文件的方法

    这篇文章主要为大家详细介绍了java多线程读取多个文件的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08

最新评论