關於java虛擬機中的垃圾回收(關於java虛擬機垃圾回收的描述中正確的是)

本文目錄一覽:

【JVM】對象分配與回收–垃圾回收機制

對象回收需要確認三件事,那些需要回收(對象存活判定,二次標記),何時回收(GC觸發條件)以及如何回收(垃圾回收算法,垃圾回收器)

1)引用計數法

2)可達性分析:GCRoots作為起始點,沿着引用鏈搜索。

GCRoots可以為:虛擬機棧中引用的對象;本地方法棧中native引用的對象;方法區中類靜態屬性引用的對象;方法區中常量引用的對象;

1)強引用:即常見的那種引用,一個引用指向堆內存中對象。

2)軟引用:還有用但非必須的,當空間不足了(即將OOM),才會對它進行回收。

3)弱引用:也是非必須的,不管空間剩餘多少,只要有垃圾回收就進行回收。

4)虛引用:一個對象是否有虛引用,不會對它的生存事件有任何影響,也無法通過虛引用獲得一個實例。唯一目的能在對象被回收時收到一個系統通知。

1)當對象被判定為不可達後,會進行一次標記,並篩選出覆蓋了finalize方法且還沒被執行過的對象進入下一步,那些沒有覆蓋的,或覆蓋但已執行過的(finalize只能執行一次)將會被回收。

2)將篩選出的對象加入一個隊列,並有一個優先級很低的Finalier線程去執行隊列中對象的finalize方法,若finalize方法中該對象重新獲得了引用,則復活,否則在第二次標記時他將被回收(第二次標記就是第一步中的標記,循環這兩步)。

1)標記清除:將不可達的對象進行標記,GC時回收、—-效率低、空間碎片

2)複製:將內存區域(堆)分為兩塊,每次只使用其中的一塊。將可達對象進行標記,複製到另一邊,然後將剛才那一邊全部清空。

這中算法適用於存活對象很少的情況,經常被用於新生代的垃圾回收。

3)標記整理:將不可達對象標記後,清除對象後將存活對象向一端移動,減少空間碎片的產生。

該算法被廣泛使用於老年代中。

4)分代收集:由於不同的對象有自己不同的特點。將區域劃分為新生代與老年代。

新生代:對象大都朝生夕死,存活時間短。每次GC只有少量對象存活。一般用 複製算法 。(複製算法一般需要空間分配擔保空間)

老年代:對象存活時間較長。也沒有額外空間對他進行分配擔保,更適合標記清除 、標記整理算法。

要注意垃圾回收器的設計目標是,有的是為了減少STW的時間,有的是為了吞吐量。這也應該作為一個選擇回收器的標準。

1)Serial / Serial Old

Serial是一個單線程的垃圾回收器,進行垃圾回收時,必然要STW。Serial在新生代採用複製算法,其對應的老年代回收器在老年代採用標記整理法。

2)ParNew 

ParNew實質上一個多線程的Serial,他能夠實現多個線程進行垃圾回收,但仍是需要用戶線程STW之後才能並發執行垃圾回收。一般開啟CPU核數個線程,用-XX:ParallelGCThreads設置。也可以用-XX:SurvivorRatio設置年輕代Eden與Survivor的比例,-XX:HandlerPromotionFailure設置空間分配擔保是否開啟。

是一種年輕代的垃圾回收器,採用複製算法。

3)Parallel Scavenge收集器 /Parallel Old

這是一款新生代收集器,也是採用複製算法。特點是關注點不在於停頓時間,而在於吞吐量。可通過時設置-XX:UseAdapatorSizePolicy為true將該收集器設為一個自適應調節策略,會自動根據吞吐量與停頓時間等因素進行自適應調節。

相關參數:設置吞吐量大小-XX: GCTimeRatio,-XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間,-XX:UseAdaptativeSizePolicy自適應的設置新生代中區域的比例

Parallel Old是它的老年代的回收器,採用多線程和標記整理算法實現,可以選擇Parallel Scavenge與Parallel Old配合使用組合為一個注重吞吐量的垃圾回收機制。

4)CMS (Concurrent Mark Sweep)老年代回收器!

由名字就可以看出來,是一款基於並發的標記清除算法的收集器,CMS收集器是針對於老年帶的收集器,以最短停頓時間為目標。

STEP    分為四個步驟,其中耗時最長的並發標記與並發清除是與用戶線程並發完成的,而其它兩個步驟會有短暫的停頓,以此種方式盡量減少停頓時間

a.初始標記:就是標記一些GCRoots的直接引用,速度非常快,這一過程會有短暫的SWT

b.並發標記:與用戶線程並發的進行GCRoots tracing,沿引用鏈去標記不可達的對象。

c.重新標記:由於第二步是與用戶線程一起發生的,有可能在回收過程中會有新的垃圾產生,這一步就是對於這些浮動垃圾進行標記(短暫停頓)

d.並發清除:與用戶線程並發進行垃圾回收,採用標記清除算法。

優缺點

並發收集、低停頓

a.對CPU資源敏感,降低吞吐量(吞吐量指用戶線程佔用時間/總運行時間,這種算法佔用了用戶現成的一部分CPU時間)(並發涉及的程序都會爭搶CPU資源)

b.標記清除算法導致的空間碎片:可能會造成大量空間浪費,若大對象(直接進入老年代)存儲時,則會由於連續空間不足引發FullGC。可通過設置-XX:UseCMSCompactAtFullCollection開關,設置是否在FullGC前執行碎片整理合併。

c.浮動垃圾:在並發階段,用戶也可能會隨時產生垃圾。在這裡需要考慮另一個問題,由於是並發的,所以我們需要考慮用戶線程需要佔用一部分存儲空間,就不可能等到老年代快滿了再回收垃圾,需預留給用戶線程一些空間。可以通過設置-XX:CMSInitialingOccupationFraction來設置觸發FullGC時內存空間使用比例,若這個參數過低,則會增加發生GC的次數;若過高,留給用戶空間的內存不夠,又會引發Concurrent Mode Failure,而啟用Serial Old收集器作為備份方案。

5)G1(Garbage first)

特點

a.將新生代與年老代同一划分為多個Region區域。

b.標記整理算法。

c.與用戶線程並發執行。

d.可預測停頓時間。因為可以有計劃的避免在整個java堆中進行全局的回收,將堆劃分為多個Region,並為每個計算Region里垃圾堆積的價值大小(回收空間大小以及時間),根據價值維護一個Region的優先列表,每次都選取列表中第一個進行回收,即回收價值最大的那個Region。

需要考慮的問題是:化整為零後,多個region之間的一些引用如何確定呢(新生代老年代也有該問題),虛擬機採用Remember Set來避免全表掃描。每個region維護一個Remember Set,在對引用類型進行寫操作時,會發生寫中斷,此時檢查該引用的對象是否在別的Region中,若是,則將該信息寫入他自己所屬Region的RememberSet中。以便在對某一個Region進行可達性分析是不需要全表掃描。

STEP

a.初始標記:僅標記GCRoot是直接引用

b.並發標記:並發的標記所有不在引用鏈上的引用

c.最終標記:在並發標記階段新產生的對象會寫入Remember set log中,在該階段將Remember Set log中的數據更新進Remember set中,進行標記。

d.篩選回收:!!!不是並發的,對Region的價值進行排序,並根據用戶希望的GC停頓時間制定回收計劃,由於只回收一個Region,速度也很快,就沒有採取並發。

從前面已經知道了,我們根絕對象生存時間的長短不一這一特徵,將堆劃分為年輕代和年老代。這裡進一步了解年輕代內部劃分,以及分配對象時區域之間是如何協助的,從而進一步能夠知道GC觸發的時間。

1)年輕代的劃分與基本工作機制

年輕代一般採用複製算法,將年輕代劃分為Eden區與FromSurvivor、ToSurvivor。分配對象時,首先分配到Eden與FromSurvivor區中,然後將可達的對象複製到ToSurvivor區中,注意此時將這些對象的年齡+1,並清除Eden+FromS中的無用對象。然後再將FromS變為ToS,ToS變為FromS,(也省去了將To中的對象複製給From中這一步驟)以便下一次的對象回收。

2)對象分配的機制

a. 一般對象首先分配給Eden區,若空間不足則會觸發MinorGC。

b. 大對象會直接分配到年老代,若空間不足觸發FullGC。

c. 對象在幾個情況下將從年輕代進入年老代:

        c1:對象年齡到了(默認為15,在複製過程中年齡增加,因為可達而熬過了一次GC嘛)可通過-XX:MaxTenuringThreshole設置進入年老代的年齡。

        c2:動態對象年齡判斷:當年輕代中同一年齡的對象所佔存儲空間之和大於 Survivor 的一半時(佔滿了toSurvivor?),將大於等於該年齡的對象都放入年老代

        c3:空間分配擔保:年輕代採用複製算法,就需要有空間在survivor區裝不下存活對象的時候幫忙存儲多出來的對象(一般發生於一次MinorGC之前,存活對象過多導致survivor放不下了),年老代即為空間分配擔保的空間。發生空間分配擔保時,會進入老年代。

3)空間分配擔保機制

年老代進行空間分配擔保是有風險的,若擔保過來的對象超過了剩餘空間,那麼年老代會發生FullGC。

所以在發生MinorGC之前,虛擬機會判斷新生代所有對象總空間(!!一次保障性的對比,若成立則其子集肯定成立)是否小於年老代最大連續空間大小,若小於,則擔保成功,認為這次MinorGC是安全的,執行GC。若大於,則要判斷年老代是否開啟HandlePromotionFailure(是否允許空間分配擔保),若沒開啟,則直接執行FullGC。若開啟了,則判斷以往平均擔保大小是否小於最大連續空間大小,若小於,則嘗試MinorGC(有風險,失敗了再FullGC,多一次MinorGC),若大於,則FullGC(包括MinorGC)。

4)總結一下GC觸發條件與設置 

MinGC:當Eden區不夠時,可設置-XX:SurvivorRatio設置年輕代中Eden的比例,-XX:NewRatio設置堆中年輕代比例,-Xms  -Xmx設置堆最小最大值。

FullGC:a.System.gc()

                b.永久代,類、靜態變量,常量太多,不夠放  -XX:MaxPermSize

                c.老年代:c1大對象直接進入時不夠 c2空間分配擔保,MinorGC前,不願意擔保直接FullGC,願意擔保且空間小於之前擔保平均值FullGC,若大於但擔保失敗了(這次太多了)先MinorGC(發現不行)再FullGC。

                d,CMS回收器:只針對老年代收集,浮動垃圾過多,當超過設置的比例大小時(因為用戶線程也有空間),會FullGC。設置老年代空間使用比例

-XX:CMSInitialingOccupationFraction,到達這麼多時FullGC

目前了解的調優方式:

1)頻繁GC時,要使用JDK一些工具排查問題具體位置。

jstat:虛擬機各方面的運行數據  jmap:生成內存轉儲快照  一般通過這兩個就能定位堆中的問題,判斷是空間設置不合理還是程序問題。

跟着GC出發點走,調整各個區域大小-Xms -Xmx -XNewRatio -XX:SurvivorRatio  -XX:permSize,以及年老代相關的 maxTenuringTreshold,handlePromotionFailure(一般設為true,以減少FullGC,給MinorGC機會嘛)

2)性能問題,要根據需求選擇合適的垃圾回收器,以停頓時間為目標還是以吞吐量為目標

如何通知java虛擬機進行垃圾回收?以及垃圾回收機制的原理是什麼

java的垃圾回收會由虛擬機自動進行。因為各版本虛擬機的實現不一樣,具體回收時點會有一定的不同,但大體上在對內存不足時,是一定會嘗試進行一次回收的。如果回收後,內存還是不夠,則會報出經典的OutofMemory異常。

用戶可以調用System.gc()進行強制的內存回收,但和上面一樣,回收完後不一定就保證能有足夠的內存。

具體原理你可以想象為虛擬機會保存一張森林結構的內存對象表,林中各樹的根節點是各個線程,線程中引用的對象,以及這些對象引用的其他對象會按照引用關係依次排列分布到樹中。這樣當GC進行時,依次掃描所有對象,如果一個對象的父引用指向不到一個處於活動狀態的線程,或者所有直接父引用已經標記為可回收,則將這個對象標記為可回收。最後再釋放所有標記為可回收的對象內存,達到清理內存垃圾的目的。

Java虛擬機垃圾回收機制是怎樣的?

System.gc() 可以建議jvm進行垃圾回收,不過這個可以通過配置禁止掉。

一個後台線程在資源不足或一定時間後回收廢棄的對象佔用的內存。

java中垃圾回收器讓工作線程停頓下來是怎麼做的?

1、jvm中,在執行垃圾收集算法時,Java應用程序的其他所有除了垃圾收集收集器線程之外的線程都被掛起。此時,系統只能允許GC線程進行運行,其他線程則會全部暫停,等待GC線程執行完畢後才能再次運行。這些工作都是由虛擬機在後台自動發起和自動完成的,是在用戶不可見的情況下把用戶正常工作的線程全部停下來,這對於很多的應用程序,尤其是那些對於實時性要求很高的程序來說是難以接受的。 但不是說GC必須STW(Stop-The-World,全局暫停), 你也可以選擇降低運行速度但是可以並發執行的收集算法,這取決於你的業務。

深入理解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 回收停頓時間。

java垃圾回收是什麼?

垃圾回收叫做GC:garbage collection,是java虛擬機JVM提供的一種內存回收方式。開發者在使用java開發的時候不需要像使用C/C++一樣去手動釋放內存,虛擬機會通過自動檢測的方式,去釋放內存。比如你用java new了一個對象,這個時候你得到了一個強引用,strong reference。當你不在需要這個對象的時候,你只需要把它設置成null。這個時候JVM並不會立刻回收掉這塊內存,在之後的某個時間點,當JVM檢測內存的時候發現這塊內存已經沒有任何強引用了,就會把它釋放掉。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/306655.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2025-01-02 12:01
下一篇 2025-01-02 12:01

相關推薦

  • Python的垃圾回收機制

    本文將對Python的垃圾回收機制進行詳細闡述,着重介紹它的基本原理和實現方式。此外,我們還將介紹常見的問題及解決方法,並給出相應的代碼示例。 一、Python的垃圾回收概述 垃圾…

    編程 2025-04-27
  • Docker 垃圾電腦的解決方案

    Docker 是一種輕量級的容器化技術,可以在一個操作系統中,同時運行多個獨立的應用。在使用 Docker 的過程中,可能會出現 Docker 佔用大量硬盤空間,導致電腦變得極其緩…

    編程 2025-04-27
  • Python垃圾回收的實現機制與優化

    一、垃圾回收工作的原理 Python解釋器採用了自動內存管理機制,即通過垃圾回收來自動管理內存。垃圾回收是python的一項基礎服務,用於回收那些無用的內存。Python中的垃圾回…

    編程 2025-04-25
  • YModem協議在上位機中的應用

    一、YModem協議概述 YModem協議是一種常用於串口通信中的文件傳輸協議。它支持數據校驗和數據重傳功能,使得數據傳輸更加穩定可靠。YModem協議有多種不同的實現方式,其中最…

    編程 2025-04-25
  • 如何在Linux上安裝虛擬機

    一、安裝虛擬機前的準備 在安裝虛擬機之前,首先需要確認以下幾點準備工作: 1、檢查CPU是否支持虛擬化技術。命令:grep -E “(vmx|svm)” /…

    編程 2025-04-24
  • Java 垃圾回收器詳解

    一、垃圾回收器概述 Java 的垃圾回收機制可以自動回收程序中不用的對象,使開發者從手動釋放內存的繁瑣任務中解脫出來。Java垃圾收集器運行於Java虛擬機中,負責回收內存中的無用…

    編程 2025-04-24
  • 深入了解 XP 虛擬機

    一、XP 虛擬機的概述 XP 虛擬機是一種能夠運行在 Windows 系統中的虛擬機軟件,它的作用是模擬一個完整的計算機環境,用戶可以在虛擬機中安裝操作系統及其應用程序,實現多個操…

    編程 2025-04-23
  • KVM虛擬機和VMware區別詳解

    一、虛擬化技術概述 虛擬化技術指將計算機資源(如CPU、內存、存儲等)進行抽象、分離,並將多個虛擬化資源分配給多個獨立的虛擬機使用的技術。 虛擬化技術可以提高硬件資源的利用率、降低…

    編程 2025-04-23
  • Ubuntu虛擬機擴容完全指南

    一、調整虛擬機磁盤空間大小 擴容一個Ubuntu虛擬機的第一步是要調整虛擬機磁盤空間大小。這可以通過VMware Workstation或VirtualBox等軟件完成。下面以VM…

    編程 2025-04-23
  • Mobaxterm如何連接虛擬機Linux

    作為一名開發工程師,如何遠程連接虛擬機Linux是我們經常要面對的問題。本文將從多個方面詳細闡述使用Mobaxterm連接虛擬機Linux的方法和注意事項。 一、選擇合適的遠程連接…

    編程 2025-04-22

發表回復

登錄後才能評論