詳解Spring如何避免被JVM 垃圾回收
回顧一下,垃圾回收的基礎(chǔ)知識
在Java堆中,存放著所有Java的對象實例。在進行垃圾收集之前,JVM需要確定哪些對象已經(jīng)不被使用(即垃圾),哪些對象仍然被使用。為了判斷對象是否是“垃圾”,JVM采用了可達性分析算法。
可達性分析算法 是指通過指定 GC Root 根對象,從根對象開始搜索引用的對象,通過引用鏈條,層層遍歷鏈條上的對象,可以到達的對象不可被垃圾回收。而最終沒有被搜索遍歷到的對象,則為 不可達對象,應(yīng)該被垃圾回收。
JVM中的 GC Root根對象包括如下:
- 虛擬機棧引用的對象
- 本地方法棧內(nèi)JNI(本地方法)引用的對象
- 方法區(qū)中類靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- Java虛擬機內(nèi)部的引用
如果 Spring 想不被垃圾回收,那么Spring一定要確保自己被以上 GC Root引用,以上五個,任意一個即可。
接下來,我們將分析Spring 源碼!找到Spring不被垃圾回收的奧秘!
啟動Spring Boot應(yīng)用
以下代碼啟動了一個Spring Boot應(yīng)用,這是官方推薦的啟動方式,通過注解的方式,把啟動類傳遞給 SpringApplication ,調(diào)用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應(yīng)用并不會退出。因為Java進程退出條件之一是:所有非守護線程全部退出,則JVM退出,所以只有 main 線程退出,其他業(yè)務(wù)線程還存在情況下,Java不會退出。
由此可見,main 線程在啟動 Spring Boot后,并不會一直持有 Spring boot 對象引用,官方文檔里也沒有 強調(diào),一定要保持 main 線程 不退出。Spring Boot需要把自己交到其他對象手中,確保自己不被回收!
那么如何保證 Spring Boot不被垃圾回收呢?我需要從 SpringApplcaition
內(nèi)部找原因!
Spring Boot 和 Tomcat
從上面的代碼可以看到 Spring Boot控制了Java應(yīng)用的入口,而Web容器 Tomcat等被Spring 管理,如果Spring不會被垃圾回收,那么Tomcat就不用擔(dān)心被垃圾回收。
而在Spring Boot之前的Web應(yīng)用,都是將Java項目打包到Tomcat容器中執(zhí)行。那時候 Spring MVC和Spring 是要被Tomcat容器管理的,所以那時的Spring項目不用擔(dān)心 被垃圾回收的問題。
而現(xiàn)在 Tomcat和 Spring boot的角色互換,決定了 Spring Boot應(yīng)用必須要處理好垃圾回收問題!
探究Spring Boot代碼
創(chuàng)建和啟動上下文
下圖是 SpringApplication.run方法的源代碼,run 方法主要執(zhí)行兩步,創(chuàng)建 Spring 上下文和啟動上下
刷新上下文的奧秘
在Spring Boot刷新上下文的代碼中,首先調(diào)用 Spring Application.refresh方法啟動Spring 上下文。然后 Spring boot就把 Close 方法注冊到 Java shutdownHook 關(guān)閉鉤子程序中!
基本上可以破案了!
因為Spring Boot控制了Java程序的入口,所以要負(fù)責(zé)整個項目的關(guān)閉流程,于是它 注冊了Java關(guān)閉鉤子。
接下來,我們看一下注冊關(guān)閉鉤子,會被 GC Root引用到嗎?
關(guān)閉鉤子
add 方法,將鉤子程序注冊到 一個容器中!
可以看到 Thread 類型的 鉤子程序,被保存在 hooks Map 中。
而hooks列表的類型定義是 static 類型的。 static 變量都是GC Root。
總結(jié)
Spring Boot 在啟動時會將關(guān)閉流程注冊到 Java 關(guān)閉鉤子中,并通過關(guān)閉鉤子線程引用到 Spring 上下文。
關(guān)閉鉤子會被保存在一個 static 靜態(tài)類型的 Map 中,這個 Map 在 GC Root 上。
因此,Spring Boot 不被垃圾回收的關(guān)鍵是在啟動時注冊了關(guān)閉鉤子。
破案了,spring永遠不會被垃圾回收。
以上就是詳解Spring如何避免被JVM 垃圾回收的詳細內(nèi)容,更多關(guān)于Spring避免被JVM垃圾回收的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring中使用@Autowired注解無法注入的情況及解決
這篇文章主要介紹了spring中使用@Autowired注解無法注入的情況及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09PowerJob的AbstractScriptProcessor實現(xiàn)類工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的AbstractScriptProcessor源碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01Java8?LocalDateTime時間日期類使用實例詳解
本文從 LocalDateTime 類的創(chuàng)建、轉(zhuǎn)換、格式化與解析、計算與比較以及其他操作幾個方面詳細介紹了 LocalDateTime 類在 Java 8 中的使用,感興趣的朋友跟隨小編一起看看吧2024-03-03