當前位置:首頁 » 操作系統 » gc各種演算法

gc各種演算法

發布時間: 2023-07-05 16:31:54

① GC垃圾回收(3)- 三色標記演算法

CMS過程在上篇文章 GC垃圾回收(2) 中已經寫過。
它分為四個階段:

其中 並發標記 階段會有漏標的問題,為解決這個問題,採用了 "三色標記演算法"

G1 GC(Garbage First Garbage Collector)是一種服務端應用使用的垃圾收集器,目標是用在 多核、大內存 的機器上,它在大多數情況下可以實現指定的GC暫停時間,同時還能保持較高的吞吐量。它的吞吐量相較PS+PO降低了大概10%~15%,但是大大降低了響應時間,大概200ms的程度

G1內存模型如下:

G1相較之前其它的垃圾回收器,對模型進行了改變,不再進行物理分代,採用邏輯分代。

它不再將連續內存分為Eden區和Old區,而是將內存分為一個個的Region。一塊Region(分區)在邏輯上依然分代,分為四種:Eden,Old,Survivor,Humongous(大對象,跨多個連續的Region)。

它的每個分區都可能是年輕代也可能是老年代,但是在同一時刻只能屬於某個代。

年輕代、倖存區、老年代這些概念還存在,成為了邏輯上的概念,這樣方便復用之前分代框架的邏輯。在物理上不需要連續,這帶來了額外的好處——有的分區內垃圾對象特別多,有的分區內垃圾對象很少,G1會優先回收垃圾對象特別多的分區,這樣可以花費較少的時間來回收這些分區的垃圾,這也就是G1名字的由來,即首先回收垃圾最多的分區。

新生代其實並不適用於這種演算法,依然是在新生代滿了的時候,對整個新生代進行回收——整個新生代中的對象,要麼被回收、要麼晉升,至於新生代也採取分區機制的原因,則是因為這樣跟老年代的策略統一,方便調整代的大小。

G1還是一種帶壓縮的收集器,在回收老年代的分區時,是將存活的對象從一個分區拷貝到另一個可用分區,這個拷貝的過程就實現了局部的壓縮。每個分區的大小從1M到32M不等,但都是2的冪次方。

特點:

G1與CMS在並發收集時的演算法沒太大區別,用的是 三色標記 演算法。但ZGC和Shenandoah使用的是 顏色指針 Colored Pointers。

主要用於分代模型中幫助垃圾回收。

為什麼需要 card table ?
尋找存活對象並不是一件容易的事。從一個GC root對象尋找,可能被Old區對象引用,這個Old區對象又被Eden區對象引用,那麼判斷Eden區對象是否存活就需要遍歷整個Old區存活對象看是否被Old區對象引用。這樣的話每進行一次YGC就要掃描整個Old區。

所以JVM內部,將內存區域分為一個個的card,對象存在一個個的card里。當老年代某個card中的對象指向了年輕代,就會將這個card標記為 Dirty 。這么多card具體哪個是 Dirty的,用點陣圖BitMap來代表(如0110010010,1表示Dirty),這就是Card Table。

Card Table :由於做YGC時,需要掃描整個Old區,效率非常低,所以JVM設計了Card Table, 如果一個Old區Card Table中有對象指向Y區,就將它設為Dirty,下次掃描時,只需要掃描Dirty Card。 在結構上,Card Table用BitMap來實現。

RSet會佔用一定的空間,所以ZGC又做了改進,不使用RSet,用顏色指針來標記。

Rset與賦值的效率:

5% ~ 60%(新生代)

G1能跟蹤STW停頓時間,根據停頓時間動態調整新生代(Y區)比例

超過單個region的 50% 就是一個大對象,也可跨越多個region。

注意: G1也是存在FGC的,並且一定會被觸發。當對象分配不下是會產生FGC。

回收時不分新生代還是老年代什麼的,region滿了就回收。

MixedGC過程:

跟CMS非常像,MixedGC最後是篩選回收,多了個篩選步驟。篩選就是找出垃圾最多的region。篩選後將存活對象復制到其他region,再將之前的region清空。

CMS和G1在並發標記時使用的是同一個演算法: 三色標記法 ,使用白灰黑三種顏色標記對象。白色是未標記;灰色自身被標記,引用的對象未標記;黑色自身與引用對象都已標記。

在remark過程中,黑色指向了白色,如果不對黑色重新掃描,則會漏標。會把白色D對象當作沒有新引用指向從而回收掉。

並發標記過程中,Mutator刪除了所有從灰色到白色的引用,會產生漏標。此時白色對象應該被回收

產生漏標問題的條件有兩個:
1.黑色對象指向了白色對象
2.灰色對象指向白色對象的引用消失

所以要解決漏標問題,打破兩個條件之一即可:

為什麼G1採用SATB而不用incremental update?
因為採用incremental update把黑色重新標記為灰色後,之前掃描過的還要再掃描一遍,效率太低。
G1有RSet與SATB相配合。Card Table里記錄了RSet,RSet里記錄了其他對象指向自己的引用,這樣就不需要再掃描其他區域,只要掃描RSet就可以了。
也就是說 灰色-->白色 引用消失時,如果沒有 黑色-->白色,引用會被push到堆棧,下次掃描時拿到這個引用,由於有RSet的存在,不需要掃描整個堆去查找指向白色的引用,效率比較高。SATB配合RSet渾然天成。

② GC是如何是如何啟動及GC中的演算法等

首先,我們先看看GC處理的內存區域在hotSpot(jdk1.8用的這個虛擬機)中是如何劃分的.
虛擬機將內存劃分為兩大區域,新生代與老年代.
而在新生代中
虛擬機又將區域劃分為Eden和兩塊survivor,新創建的對象將會在佔有較大區域的Eden和一塊survivor,當GC處理垃圾是首先進行標記,會將剩餘的存活對象復制之後放在另外survivor中,然後進行清除,清除之會.
那麼這里就有一個問題,如果存活對象過多 survivor放不下怎麼辦?
這里就會使用擔保:將溢出的對象放入老年代之中.
如果是老年代那,因為老年代的對象的存活能力很強,且無法有空間為老年代進行擔保,所以老年代使用的事 標記-整理
演算法進行垃圾回收的,當GC標記清除了可回收的對象,會將剩餘對象向一端移動.
那麼GC是如何一步步的進行垃圾回收的那?
首先GC要啟動可達性演算法,那麼GC是如何快速的找出所有的GCROOTS節點的那?
1. hotSpot是使用OopMap這樣一組數據結構進行記錄的,類載入完成之後
會記錄對象內什麼偏移量是什麼類型(書上的話),編譯器也會記錄棧和寄存機中的位置.,而這個數據會在特點的位置進行記錄,這些位置就叫做安全點(safepoint).
還有 GC的啟動需要暫時掛起所有的線程,那麼GC是在什麼時間進行垃圾回收的那?
1.當線程運行到safepoint的時候才會進行GC,那麼GC開始之後需要掛起所有線程,這是GC會選擇使用主動式的搶斷,也就是說GC會設置一個和安全點重合的輪詢點,讓所有線程都去訪問這個輪詢點,如果線程訪問結果為真,那麼就代表線程到了輪詢點,便會記性線程中斷了.

java常見gc演算法有哪些

1:標記—清除 Mark-Sweep
過程:標記可回收對象,進行清除
缺點:標記和清除效率低,清除後會產生內存碎片
2:復制演算法
過程:將內存劃分為相等的兩塊,將存活的對象復制到另一塊內存,把已經使用的內存清理掉
缺點:使用的內存變為了原來的一半
進化:將一塊內存按8:1的比例分為一塊Eden區(80%)和兩塊Survivor區(10%)
每次使用Eden和一塊Survivor,回收時,將存活的對象一次性復制到另一塊Survivor上,如果另一塊Survivor空間不足,則使用分配擔保機制存入老年代
3:標記—整理 Mark—Compact
過程:所有存活的對象向一端移動,然後清除掉邊界以外的內存

4:分代收集演算法
過程:將堆分為新生代和老年代,根據區域特點選用不同的收集演算法,如果新生代朝生夕死,則採用復制演算法,老年代採用標記清除,或標記整理
面試的話說出來這四種足夠了

④ jdk8中,GC用到的的演算法有哪些

主要應用Mark-sweepalgorithm(標記消除演算法)即從根object(程序直接訪問的)開始標記可到達的object演算法基於有向圖,採用深度優先搜索最後推薦你一本書,《java程序設計語言》([美]KenArnold,JamesGosling,DavidHolmes)上面有對

⑤ 如何設置java gc回收演算法

在java和c#語言中,使用的是託管代碼,不像c++語言那樣由程序員進行內存的手動分配和回收,java語言則由JVM即Java虛擬機 全權負責堆內存的管理,這樣子大大減少了程序員的負擔,同時一定程度上提高了開發效率和系統穩定性,而常用的GC垃圾回收演算法有哪些呢?

Java的堆是一個運行時數據區,類的實例(對象)從中分配空間。Java虛擬機(JVM)的堆中儲存著正在運行的應用程序所建立的所有對象,這些對象通過new、newarray、anewarray和multianewarray等指令建立,但是它們不需要程序代碼來顯式地釋放。一般來說,堆的是由垃圾回收 來負責的,盡管JVM規范並不要求特殊的垃圾回收技術,甚至根本就不需要垃圾回收,但是由於內存的有限性,JVM在實現的時候都有一個由垃圾回收所管理的堆。垃圾回收是一種動態存儲管理技術,它自動地釋放不再被程序引用的對象,按照特定的垃圾收集演算法來實現資源自動回收的功能。

⑥ 深入理解GC垃圾回收機制

在我們程序運行中會不斷創建新的對象,這些對象會存儲在內存中,如果沒有一套機制來回收這些內存,那麼被佔用的內存會越來越多,可用內存會越來越少,直至內存被消耗完。於是就有了一套垃圾回收機制來做這件維持系統平衡的任務。

1.確保被引用對象的內存不被錯誤的回收
2.回收不再被引用的對象的內存空間

給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時, 計數器值就減1;任何時刻計數器為0的對象就是不可能再被使用的。

優點:引用計數收集器可以很快地執行,交織在程序的運行之中。
缺點:很難處理循環引用,比如上圖中相互引用的兩個對象,計數器不為0,則無法釋放,但是這樣的對象存在是沒有意義的,空占內存了。

引用計數法處理不了的相互引用的問題,那麼就有了可達性分析來解決了這個問題。

從GC Roots作為起點,向下搜索它們引用的對象,可以生成一棵引用樹,樹的節點視為可達對象,反之最終不能與GC Roots有引用關系的視為不可達,不可達對象即為垃圾回收對象。

我自己的理解是,皇室家族每過一段時間,會進行皇室成員排查,從皇室第一代開始往下找血緣關系的後代,如果你跟第一代皇室沒有關系,那麼你就會被剔除皇室家族。

1.虛擬機棧中引用的對象(正在運行的方法使用到的變數、參數等)
2.方法區中類靜態屬性引用的對象(static關鍵字聲明的欄位)
3.方法區中常量引用的對象,(也就是final關鍵字聲明的欄位)
4.本地方法棧中引用的對象(native方法)

1.顯示地賦予某個對象為null,切斷可達性

在main方法中創建objectA、objectB兩個局部變數,而且相互引用。相互引用直接調System.gc()是回收不了的。而將兩者都置為null,切斷相互引用,切斷了可達性,與GCRoots無引用,那麼這兩個對象就會被回收調。

2.將對象的引用指向另一個對象

這里將one的引用也指向了two引用指向的對象,那麼one原本指向的對象就失去了GCRoots引用,這里就判斷該對象可被回收。

3.局部對象的使用

當方法執行完,局部變數object對象會被判定為可回收對象。

4.只有軟、弱、虛引用與之關聯
new出來的對象被強引用了,就需要去掉強引用,改為弱引用。被弱引用之後,需要置空來幹掉強引用,達到隨時可回收的效果。

只被軟引用的對象在內存不足的情況,可能會被GC回收掉。

只被弱引用持有的對象,隨時都可能被GC回收,該對象就為可回收對象。

是不是被判定為了可回收對象,就一定會被回收了呢。其實Ojbect類中還有一個finalize方法。這個方法是對象在被GC回收之前會被觸發的方法。

該方法翻譯過來就是:當垃圾回收確定不再有對該對象的引用時,由垃圾回收器在對象上調用。子類重寫finalize方法以處置系統資源或執行其他清除。說人話就是對象死前會給你一個迴光返照,讓你清醒一下,想干什麼就干什麼,甚至可以把自己救活。我們可以通過重寫finalize方法,來讓對象復活一下。

示例:

執行的結果:

這里我們重寫FinalizeGC類的finalize方法, 使用FinalizeGC.instance = this語句,讓對象又有了引用,不再被判定為可回收對象,這里就活了。然後再置空再回收一下,這個對象就死了,沒有再被救活了。所以finalize方法只能被執行一次,沒有再次被救活的機會。

在JDK1.8版本廢棄了永久代,替代的是元空間(MetaSpace),元空間與永久代上類似,都是方法區的實現,他們最大區別是:元空間並不在JVM中,而是使用本地內存。
元空間有注意有兩個參數:

MetaspaceSize :初始化元空間大小,控制發生GC閾值
MaxMetaspaceSize : 限制元空間大小上限,防止異常佔用過多物理內存
為什麼移除永久代?
移除永久代原因:為融合HotSpot JVM與JRockit VM(新JVM技術)而做出的改變,因為JRockit沒有永久代。
有了元空間就不再會出現永久代OOM問題了!

1.Generational Collection(分代收集)演算法
分代收集演算法是GC垃圾回收演算法的總綱領。現在主流的Java虛擬機的垃圾收集器都採用分代收集演算法。Java 堆區基於分代的概念,分為新生代(Young Generation)和老年代(Tenured Generation),其中新生代再細分為Eden空間、From Survivor空間和To Survivor空間。 (Survivor:倖存者)

分代收集演算法會結合不同的收集演算法來處理不同的空間,因此在學習分代收集演算法之前我們首先要了解Java堆區的空間劃分。Java堆區的空間劃分在Java虛擬機中,各種對象的生命周期會有著較大的差別。因此,應該對不同生命周期的對象採取不同的收集策略,根據生命周期長短將它們分別放到不同的區域,並在不同的區域採用不同的收集演算法,這就是分代的概念。

當執行一次GC Collection時,Eden空間的存活對象會被復制到To Survivor空間,並且之前經過一次GC Collection並在From Survivor空間存活的仍年輕的對象也會復制到To Survivor空間。

對象進入到From和To區之後,對象的GC分代年齡ege的屬性,每經過GC回收存活下來,ege就會+1,當ege達到15了,對象就會晉級到老年代。

2.Mark-Sweep(標記-清除)演算法
標記清除:標記階段的任務是標記出所有需要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。標記-清除演算法主要是運用在Eden區,該區對象很容易被回收掉,回收率很高。

3.Copying(復制)演算法
復制演算法的使用在From區和To區,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。

缺點:可使用內存縮減為一半大小。

那麼復制演算法使可使用內存大小會減半,設計上是怎麼解決這個問題的呢。就是給From和To區劃分盡可能小的區域。經過大數據統計之後,對象在第一次使用過後,絕大多數都會被回收,所以能進入第一次復制演算法的對象只佔10%。那麼設計上,Eden、From、To區的比例是8:1:1,絕大多數對象會分配到Eden區,這樣就解決了復制演算法縮減可用內存帶來的問題。

4.Mark-Compact (標記—整理)演算法
在新生代中可以使用復制演算法,但是在老年代就不能選擇復制演算法了,因為老年代的對象存活率會較高,這樣會有較多的復制操作,導致效率變低。標記—清除演算法可以應用在老年代中,但是它效率不高,在內存回收後容易產生大量內存碎片。因此就出現了一種標記—整理演算法,與標記—清除演算法不同的是,在標記可回收的對象後將所有存活的對象壓縮到內存的一端,使它們緊湊地排列在一起,然後對邊界以外的內存進行回收,回收後,已用和未用的內存都各自一邊。

垃圾收集演算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現:
Serial 收集器(復制演算法): 新生代單線程收集器,標記和清理都是單線程,
優點是簡單高效;
Serial Old 收集器 (標記-整理演算法): 老年代單線程收集器,Serial 收集器
的老年代版本;
ParNew 收集器 (復制演算法): 新生代收並行集器,實際上是 Serial 收集器
的多線程版本,在多核 CPU 環境下有著比 Serial 更好的表現;
CMS(Concurrent Mark Sweep)收集器(標記-清除演算法): 老年代並行
收集器,以獲取最短回收停頓時間為目標的收集器,具有高並發、低停頓
的特點,追求最短 GC 回收停頓時間。

熱點內容
sqlserver導出bak 發布:2025-03-18 05:29:39 瀏覽:367
騰訊穿越火線游戲如何安卓轉蘋果 發布:2025-03-18 05:10:22 瀏覽:541
安卓餓了么京東支付密碼是什麼 發布:2025-03-18 05:09:45 瀏覽:610
編程計算機編程學習 發布:2025-03-18 05:06:40 瀏覽:967
編譯和連接分別是什麼出錯 發布:2025-03-18 04:59:39 瀏覽:562
網路或者伺服器錯誤是怎麼回事 發布:2025-03-18 04:52:26 瀏覽:299
電腦伺服器燒掉 發布:2025-03-18 04:48:17 瀏覽:577
郵箱怎麼加密碼保護 發布:2025-03-18 04:37:30 瀏覽:574
雲伺服器老是半夜崩潰白天恢復 發布:2025-03-18 04:37:29 瀏覽:926
如何看自己手機配置是多少 發布:2025-03-18 04:32:26 瀏覽:857