一、GC
第一步:判斷物件是否已死?有兩種方法:第一種是參考計數法,即給物件添加一個參考計數器,當被參考時,計數器就+1;當參考失效時,就-1;當計數器為0時,代表物件沒有被參考,但是計數器的缺點就是:物件之間相互參考時導致計數器不為零,無法被回收,第二種方法是可達性分析法,即通過定義一系列的GC Roots物件作為起始點,從這些起點向下搜索,當一個物件到GC Roots沒有任何參考鏈時,則此物件是不可用的,
可以作為GC Roots的物件包括:虛擬機堆疊中參考的物件、方法去種類靜態屬性或常量參考的物件,本地方法堆疊中native方法參考的物件,
這里再說明一下參考!!!
參考分為 --> 強參考:把一個物件賦給一個參考變數,這個參考變數就是強參考,(強參考的物件不會被回收),當記憶體空間不足,Java虛擬機寧愿拋棄OutOfMemoryError錯誤,使程式例外終止,也不會靠隨意回收具有強參考的物件來解決記憶體不足的問題,強參考其實就是我們平時 A a = new A()這個意思,
軟參考:用來描述一些還有用但非必需的物件,(在系統將要發生溢位例外之前,將這些物件列入回收范圍進行二次回收,如果回收之后還沒有足夠記憶體,才會拋出例外,利用SoftReference類實作軟參考),軟參考可用來實作記憶體敏感的高速快取,
弱參考:用來描述非必需的物件,它的強度較軟參考弱,(當發生GC時,無論當前記憶體是否足夠,都會回收被弱參考關聯的物件,利用WeakReference類來實作弱參考)
虛參考:主要作用是跟蹤物件被垃圾回收的狀態,是最弱的參考關系,(PhantomReference類來實作虛參考)
一個物件的死亡,至少要經歷兩次標記的程序:如果物件在進行可達性分析后發現沒有與GC Roots相連的參考鏈,那它將會被第一次標記并且進行一次篩選,篩選的條件是此物件是否有必要執行finalize()方法,如果物件沒有覆寫finalize()方法或者finalize方法已經被虛擬機呼叫過,則沒有必要執行,如果這個物件被判定為有必要執行finalize方法,那么這個物件將會放置在一個叫做F-Queue的佇列中,并在稍后由虛擬機自動建立一個低優先級的Finalizer執行緒去執行它,
finalize方法是物件逃脫死亡命運的最后一次機會,稍后GC將對F-Queue中的物件進行第二次標記,如果物件重新與參考鏈的任何一個物件建立關聯,那在第二次標記時會將它移除“即將回收”的集合;如果物件沒有逃脫,那么它就真的被回收了,
回收方法區:永久帶的垃圾收集主要回收廢棄常量和無用的類,判斷一個類是不是“無用的類”條件:該類所有的實體都已經被回收;加載該類的ClassLoader已經被回收;該類對應的java.lang.Class物件沒有在任何地方被參考,無法通過反射訪問該類方法,
第二步:垃圾收集演算法
第一種是標記-清除演算法(mark-sweep),演算法分為標記和清除兩部分:首先標記出所有要回收的物件,標記完成后統一回收所有被標記的物件,缺點:一個是標記和清除兩個程序效率不高,另一個是標記和清除之后產生大量不連續的空間碎片,導致以后分配較大物件時找不到足夠的記憶體而提前觸發GC,
第二種是復制演算法(copying),它是將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中一塊,當這一塊記憶體用完后,就將還存活的物件復制到另一塊上面,然后再把已使用過的記憶體一次清理掉,優點:解決了記憶體碎片化,實作簡單,運行高效;缺點:將記憶體縮小為原來的一半,這樣代價較大,
第三種是標記-整理演算法(mark-compact),分為標記和整理兩部分,首先標記出所有要回收的物件,將所有存活的物件都向一端移動,然后直接清理掉端邊以外的記憶體,
第四種是分代收集演算法,就是根據物件存貨周期的不同將記憶體劃分為新生代和老年代,然后根據每個年代特性采用合適的演算法,在新生代中每次都有大量物件死去,只有少量存活,那么就采用復制演算法;而老年代物件存活率高沒有額外空間對它進行擔保,就必須使用標記-清除或標記-整理演算法來回收,
第三步:垃圾收集器
Serial收集器,是單執行緒收集器,它進行GC時必須暫停其他所有的作業執行緒,直到它收集結束,這項作業實際是虛擬機在后臺自動發起和自動完成的,
ParNew收集器,就是Serial收集器的多執行緒版本,除了使用多執行緒進行垃圾收集之外,其余行為與Serial收集器一樣,
Parallel Scavenge收集器,是使用復制演算法的新生代收集器,又是并行的多執行緒收集器,它的目的是達到一個可控制的吞吐量,高效率地利用CPU時間, PS收集器提供了控制最大垃圾收集停頓時間的 -XX:MaxGCPauseMillis引數和直接設定吞吐量大小的 -XX:GCTimeRatio引數用于精確控制吞吐量,(PS收集器還有一個引數-XX:+UseAdaptiveSizePolicy,這是一個開關引數,這個開關打開后,就不用手工指定新生代的大小、Eden和Survivor區的比例、晉升老年代物件的大小等細節引數,虛擬機會根據當前系統的運行情況收集性能監控資訊動態調整這些引數,這種調節方式稱為GC自適應的調節機制),
Serial Old收集器,是單執行緒收集器,使用標記-整理演算法,是Serial收集器的老年代版本,是給Client模式下的虛擬機使用,
Paralled Old收集器,是Parallel Scavenge收集器的老年代版本,使用多執行緒和標記-整理演算法,
CMS收集器,是一種以獲取最短回收停頓時間為目標的收集器,使用多執行緒和標記-清楚演算法,
運作程序分為:初始標記(僅僅只是標記一下GC Roots能直接關聯到的物件)
并發標記(進行GC Roots Tracing 的程序)
重新標記(為了修正并發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分標記記錄)
并發清除(清除GC Roots不可達的物件,不需要暫停作業執行緒),
優點:并發收集、低停頓;
缺點:CMS收集器對CPU資源非常敏感、CMS收集器無法處理浮動垃圾、基于標記-清除演算法會產生大量的空間碎片,
G1收集器,是一款面向服務端的垃圾收集器,G1具備并行與并發、分代收集、空間整合(基于標記-整理和復制演算法實作,不會產生空間碎片)、可預測停頓的特點;G1收集器中它將Java堆劃分為多個大小相等的獨立區域,獨立使用和回收,這樣可以控制一次回收多個區域,減少一次GC所產生的停頓(區域劃分機制),G1之所以能建立可預測的停頓模型,是因為G1跟蹤各個區域里面的垃圾堆積的價值大小,在后臺維護一個優先串列,每次根據允許的收集時間,優先回收價值最大的區域(優先區域回識訓制),為了避免全堆掃描,G1使用Remembered Set來管理相關的物件參考資訊,當進行記憶體回收時,在GC根節點的列舉范圍中加入Remembered Set即可保證不對全堆掃描也不會有遺漏,
運作程序分為:初始標記(僅僅只是標記一下GC Roots能直接關聯的物件,并修改TAMS的值,讓下一階段用戶程式并發運行時,能在正確可用的region中創建物件,此階段需要停頓執行緒,但時間很短)
并發標記(從GC Root開始對堆中的物件進行可達性分析,找出存活的物件,耗時長,但可與用戶執行緒并發執行)
最終標記(為了修正并發標記期間因用戶程式繼續運行而導致標記產生變動的那一部分標記記錄并把Remembered Set Logs的資料合并到Remembered Set 中,此階段需要停頓執行緒,但可并發執行)
篩選回收(首先根據各個region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃)
并行(Parallel):指多條垃圾收集執行緒并行作業,但此時用戶執行緒仍然處于等待狀態,
并發(Concurrent):指用戶執行緒與垃圾收集執行緒同時執行,
二、記憶體分配與回收策略
大多數情況下,物件分配在新生代的Eden區上,分配規則是不固定的,取決于當前使用的垃圾收集器以及虛擬機中與記憶體相關的引數設定,
新生代:用來存放新產生的物件,占用1/3空間;新生代分為一個Eden區和兩個Survivor區(FromSurvivor和ToSurvivor);當Eden區沒有足夠空間進行分配時,虛擬機將會進行一次Minor GC,進行一次Minor GC后的物件進入FromSurvivor區,當From和Eden區沒有可用空間時,會進行第二次Minor GC,將存活的物件移到ToSurvivor區,
大物件(需要大量連續記憶體空間的物件)直接進入老年代;虛擬機提供了一個-XX:PretenureSizeThreshold引數,令大于這個設定值的物件直接在老年代分配,這樣做避免了在Eden區及兩個Survivor區之間發生大量的記憶體復制,長期存活的物件將進入老年代,如果物件在Eden區出生并且經過第一次Minor GC后仍然存活,將被移動到Survivor區,并且物件年齡設為1,物件在Survivor區中每經過一次Minor GC,年齡就+1,當它的年齡增加到一定程度時(默認為15),就將會被晉升到老年代中,物件晉升老年代的年齡閾值,可以通過引數-XX:MaxTenuringThreshold設定,
長期存活的物件進入老年代,虛擬機為每個物件定義了一個年齡計數器,如果物件經過了1次Minor GC,那么物件會進入Survivor區,之后每經過一次Minor GC,那么物件的年齡加1,直到達到閾值,物件進入老年代,
動態物件年齡判定:如果在Survivor區中相同年齡的所有物件大小總和大于Survivor區的一半,年齡大于或等于該年齡的物件可以直接進入老年代,
空間分配擔保:當出現大量物件在Minor GC后仍然存活就需要老年代進行分配擔保,把Survivor區無法容納的物件直接進入老年代中,前提是老年代本身有容納這些物件的剩余空間,
在發生Minor GC之前,虛擬機會先檢查老年代最大可用的連續空間是否大于新生代所有物件總大小或者歷次晉升到老年代物件的平均大小,如果大于就進行MinorGC,否則將進行FullGC,
面試題
說一下Java的垃圾回識訓制?
它使得Java程式員在撰寫程式的時候不再需要考慮記憶體管理,垃圾回收器通常是作為一個單獨的低級別的執行緒運行,不可預知的情況下對記憶體堆中已經死亡或者長時間沒有使用的物件進行清除和回收,程式員不能實時的呼叫垃圾回收器對某個物件或所有物件進行垃圾回收,程式員可以手動執行System.gc(),通知GC運行,但是Java語言規范并不保證GC一定會運行,
垃圾回識訓制可以用3個詞來概括:where,when和how?
where:運行時的記憶體分布情況;
when:物件何時需要被回收?也就是何時回收無效物件,已死物件的?
(答案見上面內容)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/5231.html
標籤:其他
上一篇:Java面試之synchronized 和 static synchronized
下一篇:華為 人工智能 筆經+面經 分享
