gc java root_聊聊Java的GC机制
原標題:聊聊Java的GC機制
近日,MIUI在小米全球社區(qū)發(fā)布公告,表示MIUI將在全球市場銷售的手機中預(yù)裝谷歌撥號及谷歌消息應(yīng)用程序(中國、印度、印度尼西亞等市場除外)。小米表示,小米9T Pro之后的設(shè)備在全球范圍內(nèi)都將會預(yù)裝上述兩款應(yīng)用程序,不再預(yù)裝MIUI自帶的撥號、消息等APP,該調(diào)整是為了遵守當?shù)氐碾[私政策和法律法規(guī)。
大家周一早上好,距離過年的日子已經(jīng)越來越近,新的一周繼續(xù)加油哦!
本篇文章轉(zhuǎn)載自某人Valar的博客,分享了他對Java的GC機制的相關(guān)理解,相信會對大家有所幫助!
https://juejin.im/post/5df2ec71f265da33d7442433
/ 什么是GC /
GC:垃圾回收(Garbage Collection),在計算機領(lǐng)域就是指當一個計算機上的動態(tài)存儲器(內(nèi)存空間)不再需要時,就應(yīng)該予以釋放,以讓出存儲器,便于他用。這種存儲器的資源管理,稱為垃圾回收。
有一些語言是沒有垃圾回收機制的,像C、C++,如果需要釋放無用變量內(nèi)存空間就由自己來處理。而其他的一些語言如Java、C#都支持垃圾回收器,Java虛擬機(JVM)或.NET CLR發(fā)現(xiàn)內(nèi)存資源緊張的時候,就會自動地去清理無用對象(沒有被引用到的對象)所占用的內(nèi)存空間。而我們今天主要講Java中的GC。
上面說到JVM會自動清理無用的對象,那么我們就有了疑問:
JVM清理的是哪一塊的對象?
哪些對象會被清理,為什么清理A而不清理B?
JVM又是如何清理的?
這三個問題將分別對應(yīng)接下來的3節(jié)一一解答。
/ JVM內(nèi)存結(jié)構(gòu)簡單介紹 /
我們都知道,Java代碼是要運行在虛擬機上的,而虛擬機在執(zhí)行Java程序的過程中會把所管理的內(nèi)存劃分為若干個不同的數(shù)據(jù)區(qū)域,這些區(qū)域都有各自的用途。
在《Java虛擬機規(guī)范(Java SE 8)》
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4
中描述了JVM運行時內(nèi)存區(qū)域結(jié)構(gòu)如下:
以上是Java虛擬機規(guī)范,不同的虛擬機實現(xiàn)可能會各有不同,但是一般會遵守規(guī)范。
方法區(qū):存儲已被虛擬機加載的類信息、常量、靜態(tài)變量等
堆:堆是Java 虛擬機所管理的內(nèi)存中最大的一塊。唯一目的就是存放對象實例。在虛擬機棧中存放的只是引用,而引用指向的是堆中的對象。GC主要作用的區(qū)域。
虛擬機棧:局部變量表、操作數(shù)棧等。虛擬機棧描述的是Java 方法執(zhí)行的內(nèi)存模型:每個方法被執(zhí)行的時候都會同時創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作棧、動態(tài)鏈接、方法返回地址等信息。
本地方法棧:與虛擬機棧類似,是為native方法提供服務(wù)的。
程序計數(shù)器:記錄當前線程執(zhí)行的方法執(zhí)行到了第幾行。如果線程正在執(zhí)行的是一個Java 方法,這個計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址。如果正在執(zhí)行的是Natvie 方法,這個計數(shù)器值則為空(Undefined)。
/ 可達性分析與GC Roots /
可達性分析
Java中通過可達性分析法來確定某個對象是不是“垃圾”。
該方法的基本思想是通過一系列的“GC Roots”對象作為起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。
被判定為不可達的對象要成為可回收對象必須至少經(jīng)歷兩次標記過程,如果在這兩次標記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
注意其本質(zhì)是通過找出所有活對象來把其余空間認定為“無用”,而不是找出所有死掉的對象并回收它們占用的空間。如下圖,當object5、6、7不存在到GC Roots的引用時,即不可到達GC Roots,則判定他們是不可達的。
GC Roots
對于哪些對象可以被當成GC Roots網(wǎng)上有很多種說法,有的不夠權(quán)威、有的不夠全面。
最終找到了一份eclipse的官方文檔,里面有如下的介紹:
Garbage Collection Roots
https://help.eclipse.org/2019-09/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Fconcepts%2Fgcroots.html&resultof=%22%67%61%72%62%61%67%65%22%20%22%67%61%72%62%61%67%22%20
(自己翻譯了一下,如有不準確的地方,請指出)
A garbage collection root is an object that is accessible from outside the heap. The following reasons make an object a GC root:
System Class (被boostrap 或者系統(tǒng)類加載器加載的系統(tǒng)類)
Class loaded by bootstrap/system class loader. For example, everything from the rt.jar like java.util.* .
JNI Local( 一些用戶定義jni 代碼或者jvm的內(nèi)部代碼局部變量)
Local variable in native code, such as user defined JNI code or JVM internal code.
JNI Global( jni 代碼中的全局變量)
Global variable in native code, such as user defined JNI code or JVM internal code.
Thread Block(被阻塞的線程引用的對象)
Object referred to from a currently active thread block.
Thread (正在運行的線程)
A started, but not stopped, thread.
Busy Monitor(正在等待的線程)
Everything that has called wait or notify or that is synchronized.For example, by calling synchronized(Object) or by entering a synchronized method. Static method means class, non-static method means object.
Java Local(仍然在線程的棧中的方法的傳入?yún)?shù)或方法內(nèi)部創(chuàng)建的對象)
Local variable.For example, input parameters or locally created objects of methods that are still in the stack of a thread.
Native Stack(本地方法棧中輸入或輸出參數(shù),例如,用于文件/網(wǎng)絡(luò)I/O的方法或反射的參數(shù)。)
In or out parameters in native code, such as user defined JNI code or JVM internal code. This is often the case as many methods have native parts and the objects handled as method parameters become GC roots.For example, parameters used for file/network I/O methods or reflection.
Finalizable(在回收隊列中的對象)
An object which is in a queue awaiting its finalizer to be run.
Unfinalized(覆蓋了finalize方法但是還沒有被放入回收隊列中的對象)
An object which has a finalize method, but has not been finalized and is not yet on the finalizer queue.
Unreachable(一個從任何其他根無法訪問的對象,但由Memory Analyzer Tool 標記為根,以便該對象可以包含在分析中)
An object which is unreachable from any other root, but has been marked as a root by MAT to retain objects which otherwise would not be included in the analysis.
Java Stack Frame
A Java stack frame, holding local variables.Only generated when the dump is parsed with the preference set to treat Java stack frames as objects.
Unknown
An object of unknown root type. Some dumps, such as IBM Portable Heap Dump files, do not have root information. For these dumps the MAT parser marks objects which are have no inbound references or are unreachable from any other root as roots of this type. This ensures that MAT retains all the objects in the dump.
復(fù)制代碼簡單總結(jié)下就是:
由系統(tǒng)類加載器(system class loader)加載的對象
活著的線程,包含處于等待或阻塞的線程
當前被調(diào)用的方法(Java方法、native方法)的一些參數(shù)/局部變量
方法區(qū)中靜態(tài)變量、常量引用的對象
Held by JVM - JVM由于特殊目的為GC保留的對象,但實際上這個與JVM的實現(xiàn)是有關(guān)的。可能已知的一些類型是:系統(tǒng)類加載器、一些JVM知道的重要的異常類、一些用于處理異常的預(yù)分配對象以及一些自定義的類加載器等。
/ 常見的垃圾回收算法 /
Mark-Sweep(標記-清除)算法
這是最基礎(chǔ)的垃圾回收算法,之所以說它是最基礎(chǔ)的是因為它最容易實現(xiàn),思想也是最簡單的。標記-清除算法分為兩個階段:標記階段和清除階段。標記階段的任務(wù)是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所占用的空間。具體過程如下圖所示:
優(yōu)缺點:
從圖中可以很容易看出標記-清除算法實現(xiàn)起來比較容易
但是有一個比較嚴重的問題就是容易產(chǎn)生內(nèi)存碎片,碎片太多可能會導(dǎo)致后續(xù)過程中需要為大對象分配空間時無法找到足夠的空間而提前觸發(fā)新的一次垃圾收集動作。
Copying(復(fù)制)算法
為了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面,然后再把已使用的內(nèi)存空間一次清理掉,這樣一來就不容易出現(xiàn)內(nèi)存碎片的問題。具體過程如下圖所示:
優(yōu)缺點:
這種算法實現(xiàn)簡單,運行高效且不容易產(chǎn)生內(nèi)存碎片
但是卻對內(nèi)存空間的使用做出了高昂的代價,因為能夠使用的內(nèi)存縮減到原來的一半。
Mark-Compact(標記-整理)算法
該算法標記階段和Mark-Sweep一樣,但是在完成標記之后,它不是直接清理可回收對象,而是將存活對象都向一端移動,然后清理掉端邊界以外的內(nèi)存。具體過程如下圖所示:
優(yōu)缺點:
標記-整理算法是在標記-清除算法的基礎(chǔ)上,又進行了對象的移動,因此成本更高
但是卻解決了內(nèi)存碎片的問題。
Generational Collection(分代收集)算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根據(jù)對象存活的生命周期將內(nèi)存劃分為若干個不同的區(qū)域。一般情況下將堆區(qū)劃分為老年代(Tenured Generation)和新生代(Young Generation),在HotSpot中,設(shè)計者將方法區(qū)納入也納入了GC分代收集,并為其起了一個名字,永久代(PerGen space)。
老年代:特點是每次垃圾收集時只有少量對象需要被回收,一般使用的是標記-整理(Mark-Compact)、標記-清除(Mark-Sweep)算法。
新生代:特點是每次垃圾回收時都有大量的對象需要被回收,對于新生代都采取復(fù)制(Copying)算法。
因為新生代中每次垃圾回收都要回收大部分對象,也就是說需要復(fù)制的操作次數(shù)較少,但是實際中并不是按照1:1的比例來劃分新生代的空間的,一般來說是將新生代劃分為一塊較大的Eden空間(伊甸園,亞當和夏娃偷吃禁果生娃娃的地方,用來表示內(nèi)存首次分配的區(qū)域,再貼切不過)和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象復(fù)制到另一塊Survivor空間中,然后清理掉Eden和剛才使用過的Survivor空間。一般來說分配比例為eden 80%、survivor1 10%、survivor2 10%。
永久代:方法區(qū)和永久代的關(guān)系很像Java中接口和類的關(guān)系,類實現(xiàn)了接口,而永久代就是HotSpot虛擬機對虛擬機規(guī)范中方法區(qū)的一種實現(xiàn)方式。
在方法區(qū)進行垃圾回收一般”性價比”較低, 因為在方法區(qū)主要回收兩部分內(nèi)容: 廢棄常量和無用的類。回收廢棄常量與回收其他年代中的對象類似, 但要判斷一個類是否無用需要以下條件:
該類所有的實例都已經(jīng)被回收, Java堆中不存在該類的任何實例;
該類對應(yīng)的Class對象沒有在任何地方被引用(也就是在任何地方都無法通過反射訪問該類的方法);
加載該類的ClassLoader已經(jīng)被回收。
但即使?jié)M足以上條件也未必一定會回收,Hotspot VM還提供了-Xnoclassgc參數(shù)控制(關(guān)閉CLASS的垃圾回收功能)。
參考:
https://cloud.tencent.com/developer/article/1390547
附:HotSpot虛擬機在1.8之后已經(jīng)取消了永久代,改為元空間,類的元信息被存儲在元空間中,元空間這里不過多介紹,有興趣的同學可以自己了解下。
/ 結(jié)語 /
到此關(guān)于Java GC的基本概念、JVM的內(nèi)存結(jié)構(gòu)、GC回收基本機制都已經(jīng)介紹的差不多了。有疑問或者發(fā)現(xiàn)文章中有錯誤的可以在下方留言交流。像Java垃圾收集器、元空間這里并沒有詳細的說明,之后有時間的話會單獨拿出來聊聊。返回搜狐,查看更多
責任編輯:
總結(jié)
以上是生活随笔為你收集整理的gc java root_聊聊Java的GC机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HDMI 特许公司称 HDMI 替代模式
- 下一篇: 金山文档联合媒体发布《缓解“阳康”后不适