你知道JVM中GC?Root對(duì)象有哪些嗎
JVM中GC Root對(duì)象有哪些
眾所周知,我們目前最常用的虛擬機(jī)hotspot使用可達(dá)性分析來(lái)進(jìn)行垃圾回收,而可達(dá)性分析需要依賴(lài)GC Root。
下面我就來(lái)介紹下可以作為GC Root的對(duì)象。
(一)虛擬機(jī)棧中引用的對(duì)象
虛擬機(jī)棧中的引用的對(duì)象可以作為GC Root。我們程序在虛擬機(jī)的棧中執(zhí)行,每次函數(shù)調(diào)用調(diào)用都是一次入棧。在棧中包括局部變量表和操作數(shù)棧,局部變量表中的變量可能為引用類(lèi)型(reference),他們引用的對(duì)象即可作為GC Root。不過(guò)隨著函數(shù)調(diào)用結(jié)束出棧,這些引用便會(huì)消失。
(二)方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象
簡(jiǎn)單的說(shuō)就是我們?cè)陬?lèi)中使用的static聲明的引用類(lèi)型字段,例如:
Class Dog { ? ? private static Object tail; }?
(三)方法區(qū)中常量引用的對(duì)象
簡(jiǎn)單的說(shuō)就是我們?cè)陬?lèi)中使用final聲明的引用類(lèi)型字段,例如:
Class Dog { ? ? private final Object tail; }?
(四)本地方法棧中引用的對(duì)象
就是程序中native本地方法引用的對(duì)象。
JVM 中的 GC Roots 和可達(dá)鏈
什么是GC Root 對(duì)象?
簡(jiǎn)單講,凡是被常量、靜態(tài)變量、全局變量、運(yùn)行時(shí)方法中的變量直接引用的對(duì)象,原則上不能被GC釋放。
JVM中對(duì)內(nèi)存進(jìn)行回收時(shí),需要判斷對(duì)象是否仍在使用中,可以通過(guò) GC Roots Tracing辨別。
GC Roots 定義:
通過(guò)一系列名為”GCRoots”的對(duì)象作為起始點(diǎn),從這個(gè)節(jié)點(diǎn)向下搜索,搜索走過(guò)的路徑稱(chēng)為ReferenceChain,當(dāng)一個(gè)對(duì)象到GCRoots沒(méi)有任何ReferenceChain相連時(shí),(圖論:這個(gè)對(duì)象不可到達(dá)),則證明這個(gè)對(duì)象不可用。
可以作為GC Root 引用點(diǎn)的是:
- JavaStack中的引用的對(duì)象。
- 方法區(qū)中靜態(tài)引用指向的對(duì)象。
- 方法區(qū)中常量引用指向的對(duì)象。
- Native方法中JNI引用的對(duì)象。
所謂“GC roots”,或者說(shuō)tracing GC的“根集合”,就是一組必須活躍的引用。
Tracing GC的根本思路就是:給定一個(gè)集合的引用作為根出發(fā),通過(guò)引用關(guān)系遍歷對(duì)象圖,能被遍歷到的(可到達(dá)的)對(duì)象就被判定為存活,其余對(duì)象(也就是沒(méi)有被遍歷到的)就自然被判定為死亡。注意再注意:tracing GC的本質(zhì)是通過(guò)找出所有活對(duì)象來(lái)把其余空間認(rèn)定為“無(wú)用”,而不是找出所有死掉的對(duì)象并回收它們占用的空間。
GC roots這組引用是tracing GC的起點(diǎn)。要實(shí)現(xiàn)語(yǔ)義正確的tracing GC,就必須要能完整枚舉出所有的GC roots,否則就可能會(huì)漏掃描應(yīng)該存活的對(duì)象,導(dǎo)致GC錯(cuò)誤回收了這些被漏掃的活對(duì)象。
這就像任何遞歸定義的關(guān)系一樣,如果只定義了遞推項(xiàng)而不定義初始項(xiàng)的話(huà),關(guān)系就無(wú)法成立——無(wú)從開(kāi)始;而如果初始項(xiàng)定義漏了內(nèi)容的話(huà),遞推出去也會(huì)漏內(nèi)容。
常說(shuō)的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的對(duì)象,GC會(huì)收集那些不是GC roots且沒(méi)有被GC roots引用的對(duì)象。
Java 進(jìn)行GC的時(shí)候會(huì)從GC root進(jìn)行可達(dá)性判斷,常見(jiàn)的GC Root有如下:
- 通過(guò)System Class Loader或者Boot Class Loader加載的class對(duì)象(通過(guò)自定義類(lèi)加載器加載的class不一定是GC Root)
- 處于激活狀態(tài)的線程
- 棧中的對(duì)象
- JNI棧中的對(duì)象
- JNI中的全局對(duì)象
- 正在被用于同步的各種鎖對(duì)象
- JVM自身持有的對(duì)象,比如系統(tǒng)類(lèi)加載器等。
在調(diào)查內(nèi)存泄漏原因的時(shí)候可以根據(jù)GC Root來(lái)推導(dǎo).
常用的GC算法
了解了這些,我們來(lái)看一下常用的GC算法
標(biāo)記回收算法
從GC root進(jìn)行遍歷,把可達(dá)對(duì)象都標(biāo)記,剩下那些不可達(dá)的進(jìn)行回收,這種方式需要中斷其他線程,并且可能產(chǎn)生內(nèi)存碎片
復(fù)制算法
把內(nèi)存區(qū)域分為兩塊,每次使用一塊,GC的時(shí)候把一塊中的內(nèi)容移動(dòng)到另一塊中,原始內(nèi)存中的對(duì)象就可以被回收了。
標(biāo)記壓縮算法
和標(biāo)記回收差不多,但是在回收的時(shí)候會(huì)對(duì)可達(dá)對(duì)象進(jìn)行整理,將其壓縮到內(nèi)存的一段,避免內(nèi)存碎片
分代算法
將內(nèi)存區(qū)域分代,對(duì)不同的代使用不同的回收算法,通常分為新生代,老年代,和永久帶。
新生代一般包含三個(gè)區(qū)域,Eden區(qū)和兩個(gè)Survivor區(qū),新生代一般采用復(fù)制算法
老年代一般采用標(biāo)記壓縮算法.
GC Root 對(duì)象有哪些?
JVM垃圾回收的根對(duì)象的范圍有以下幾種:
(1)虛擬機(jī)(JVM)棧中引用對(duì)象
(2)方法區(qū)中的類(lèi)靜態(tài)屬性引用對(duì)象
(3)方法區(qū)中常量引用的對(duì)象(final 的常量值)
(4)本地方法棧JNI的引用對(duì)象
一個(gè)對(duì)象可以屬于多個(gè)root,GC root有幾下種:
-
Class
由系統(tǒng)類(lèi)加載器(system class loader)加載的對(duì)象,這些類(lèi)是不能夠被回收的,他們可以以靜態(tài)字段的方式保存持有其它對(duì)象。我們需要注意的一點(diǎn)就是,通過(guò)用戶(hù)自定義的類(lèi)加載器加載的類(lèi),除非相應(yīng)的java.lang.Class實(shí)例以其它的某種(或多種)方式成為roots,否則它們并不是roots,. Thread
活著的線程Stack Local
Java方法的local變量或參數(shù)JNI Local
JNI方法的local變量或參數(shù)JNI Global
全局JNI引用Monitor Used
用于同步的監(jiān)控對(duì)象Held by JVM
用于JVM特殊目的由GC保留的對(duì)象,但實(shí)際上這個(gè)與JVM的實(shí)現(xiàn)是有關(guān)的??赡芤阎囊恍╊?lèi)型是:系統(tǒng)類(lèi)加載器、一些JVM知道的重要的異常類(lèi)、一些用于處理異常的預(yù)分配對(duì)象以及一些自定義的類(lèi)加載器等。然而,JVM并沒(méi)有為這些對(duì)象提供其它的信息,因此就只有留給分析分員去確定哪些是屬于"JVM持有"的了。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
spring security自定義認(rèn)證登錄的全過(guò)程記錄
這篇文章主要給大家介紹了關(guān)于spring security自定義認(rèn)證登錄的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12Java實(shí)現(xiàn)定時(shí)讀取json文件里內(nèi)容的示例代碼
有時(shí)候我們會(huì)需要定時(shí)來(lái)讀取JSON配置文件里的內(nèi)容,來(lái)執(zhí)行一些業(yè)務(wù)邏輯上的操作,本文就介紹了Java實(shí)現(xiàn)定時(shí)讀取json文件里內(nèi)容的示例代碼,感興趣的可以了解一下2023-08-08Java 中普通代碼塊,構(gòu)造代碼塊,靜態(tài)代碼塊區(qū)別及代碼示例
這篇文章主要介紹了Java 中普通代碼塊,構(gòu)造代碼塊,靜態(tài)代碼塊區(qū)別及代碼示例的相關(guān)資料,需要的朋友可以參考下2017-01-01java POI 如何實(shí)現(xiàn)Excel單元格內(nèi)容換行
這篇文章主要介紹了java POI 如何實(shí)現(xiàn)Excel單元格內(nèi)容換行的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot+netty-socketio實(shí)現(xiàn)服務(wù)器端消息推送
這篇文章主要介紹了SpringBoot+netty-socketio實(shí)現(xiàn)服務(wù)器端消息推送,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03java?Semaphore共享鎖實(shí)現(xiàn)原理解析
這篇文章主要為大家介紹了Semaphore共享鎖實(shí)現(xiàn)原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01maven將項(xiàng)目打包上傳到nexus私服的詳細(xì)教程
這篇文章主要介紹了maven將項(xiàng)目打包上傳到nexus私服,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-07-07