什麼是gc和gc的工作原理

一、GC是垃圾收集的意思,內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會導致程序或系統的不穩定甚至崩潰,Java提供的GC功能可以自動監測對象是否超過作用域從而達到自動回收內存的目的,Java語言沒有提供釋放已分配內存的顯示操作方法。Java程序員不用擔心內存管理,因爲垃圾收集器會自動進行管理。要請求垃圾收集,可以調用下面的方法之一:

System.gc() 或Runtime.getRuntime().gc() 。

    垃圾回收可以有效的防止內存泄露,有效的使用可以使用的內存。垃圾回收器通常是作爲一個單獨的低優先級的線程運行,不可預知的情況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。回收機制有分代複製垃圾回收、標記垃圾回收、增量垃圾回收等方式。

 

   補充:標準的Java進程既有棧又有堆。棧保存了原始型局部變量,堆保存了要創建的對象。Java平臺對堆內存回收和再利用的基本算法被稱爲標記和清除,但是Java對其進行了改進,採用“分代式垃圾收集”。這種方法會跟Java對象的生命週期將堆內存劃分爲不同的區域,在垃圾收集過程中,可能會將對象移動到不同區域:

伊甸園(Eden):這是對象最初誕生的區域,並且對大多數對象來說,這裏是它們唯一存在過的區域。

倖存者樂園(Survivor):從伊甸園倖存下來的對象會被挪到這裏。

終身頤養園(Tenured):這是足夠老的倖存對象的歸宿。年輕代收集(Minor-GC)過程是不會觸及這個地方的。當年輕代收集不能把對象放進終身頤養園時,就會觸發一次完全收集(Major-GC),這裏可能還會牽扯到壓縮,以便爲大對象騰出足夠的空間。

與垃圾回收相關的JVM參數:

-Xms / -Xmx --- 堆的初始大小 / 堆的最大大小

-Xmn --- 堆中年輕代的大小

-XX:-DisableExplicitGC --- 讓System.gc()不產生任何作用

-XX:+PrintGCDetail --- 打印GC的細節

-XX:+PrintGCDateStamps --- 打印GC操作的時間戳

二、gc工作機制

JVM:Java虛擬機的簡稱。

 

談到JVM,通常會聊到三個問題:

 

1. 什麼時候觸發Java GC?

 

2. 對什麼東西進行Java GC?

 

3. 如何進行Java GC?

 

 

首先解決第一個問題:

 

1. 什麼時候觸發Java GC?

 

GC分爲minor GC和Full GC。

 

Full GC:

 

年老代被寫滿;

    持久代被寫滿

    System.gc被顯示調用

當有新對象生成的時候,如果申請eden空間失敗的話,就會觸發mino gc,對eden去進行gc處理,清除非存活的對象,並且把尚且存活的對象移到survival區裏面。經過一定次數的對象進入老年代的時候,如果老年代的剩餘空間放不下升到老年代的對象的時候,就會觸發full gc,利用標記清除算法,將不可達的對象清楚,或者小於的時候被handlepromotionFailure參數強制full gc。具體的gc時間是由系統決定的,無法預測。

2. 對什麼東西進行GC ?對GC root搜索不到,並且經過第一次標記,清除之後,仍然沒有復活的對象進行GC。

 

3. 做了什麼工作,怎麼進行GC?

 

主要做了清理對象,整理內存的工作,Java堆中主要是有新生代和年老代,他們採用不同的回收方式,例如新生代採用了標記複製的算法(因爲其生存時間比較短),新生代進行gc的時候,會把eden區存活的對象放到另外一個survival區域裏面,然後把eden區和另外一個survival區清除。而年老代採用了標記清除的算法,首先標記出存活的對象,然後移到另一端,這樣也能減少內存碎片化。

 

 

 

 

垃圾收集有哪些算法?

 

標記清除算法:標記出需要回收的對象,標記完成後,統一回收所有被標記的對象。(兩個不足:效率不高:標記和清除都不高;空間問題:產生大量不連續的內存碎片,可能導致以後在分配較大對象的時候因無法找到連續的內存而觸發GC)

 

複製算法:(新生代)比較適合對象存活率比較低的場景。應用於新生代,它主要是把內存分成兩個塊,每次只使用一塊,就像新生代一樣,有兩個區域:一個是eden區,一個是survival區。這樣也就解決了內存碎片化的問題。

 

標記整理算法:(老年代)比較適合對象存活率比較高的場景,應用於老年代,將所有存活對象都移向另一端,然後直接清除掉端邊界外的內存。

 

 

 

如何判斷對象是否存活?

 

引用計數法:每個對象都有一個引用計數器,當對象被引用一次的時候,計數器+1,當對象引用失效的時候,計數值-1,實時性,當對象的引用計數器的值爲0,則立刻回收,不能解決循環引用的問題。

 

可達性算法分析:從GC Root作爲起點開始搜索,,那麼整個連通圖的對象都是存活的對象,對於GC Root無法到達的對象便成了垃圾回收的對象。(解決循環引用的問題)

 

 

 

常用的引用:

 

強引用>軟引用>弱引用>虛引用

 

強引用:GC永遠都不會回收的對象。內存空間不足時,寧願拋出OutOfMemoryError。

 

軟引用:內存空間不足時會考慮回收它,空間足夠的時候不會

 

弱引用:不管內存空間夠不夠,都會回收它。

 

虛引用:不會影響生存時間,目的是能在這個對象被收集器回收時收到一個系統通知。

 

 

哪些對象可以作爲GC Root對象

虛擬機棧中的對象

 

方法區的靜態變量

 

方法區常量池的對象

 

 

 

JVM垃圾收集器:

 

 

 

 

 

分爲新生代收集器,老年代收集器。

 

新生代收集器:

 

在執行機制上JVM主要提供了串行GC(serial GC),並行GC(ParNew),並行回收GC(parallel scavenge)

 

Serial GC:在整個GC的過程中採用單線程的方式來進行垃圾回收,在回收過程中,必須停止其他所有的工作線程。 適用於單CPU,是client模式下默認的GC方式。

 

parNew :其實就是serialGC的多線程版本,除了使用多條線程來進行垃圾收集之外,其他行爲跟serialGC差不多。,是server模式下默認使用的GC方式。能夠與CMS收集器配合工作。

 

Parallel scavenge:它跟parNew差不多,也是一個並行的多線程收集器。不過他的關注點跟其他收集器不一樣。Cms等收集器的關注點是儘可能的縮短垃圾收集是用戶線程的停頓時間,而parallel scavenge的目的是爲了達到一個可控制的吞吐量。(也就是CPU用於運行用戶代碼的時間與CPU總的消耗時間的比值)還有一個就是parallel scavenge有GC的自適應調節策略:我們可以通過一個參數,這個參數叫做userAdaptiveSizePolicy來讓虛擬機幫我們動態的調整這些參數以提供最合適的停頓時間或者最大的吞吐量。

 

老年代收集器:

 

 

串行GC(serial old):他是serial的老年代版本,也是一個單線程收集器,使用標記整理算法。

 

並行GC(parallel old):是parallel scavenge 的老年代版本。使用多線程和“標記-整理”算法。

 

併發GC(CMS):獲取最短回收停頓時間爲目標的收集器。

 

優點是:併發收集,低停頓

 

總結如下:

 

初始標記:標記一下GC root能夠直接關聯到的對象,速度很快

    併發標記:進行GC root tracing的過程,耗時比較長,併發,

    重新標記:爲了修正併發標記期間因爲用戶程序繼續運行而導致標記產生變動的那一部分對象的的標記記錄。

    併發清除:耗時較長,併發。

    這四個過程中,耗時最長的併發標記和併發清除過程收集器線程都可以與用戶線程一起工作,所以,整體上說:cms收集器的回收過程時與用戶線程一起工作的。

缺點有三:

 

對cpu資源敏感,因爲併發標記和併發清除過程中會佔用cpu,公式是:

(cpu數量+3)/4,當cpu<4的時候,佔用了不少於25%的cpu資源。(增量式併發收集器:讓他們交替執行)

 

無法處理浮動垃圾:在第四個階段併發清理的時候,因爲用戶線程還在運行,所以就會有新的垃圾產生,這些就是浮動垃圾,cms是無法在當次收集中處理掉他們的。(運行期間要預留內存給他們,不然會出現concurrent mode failure)

    因爲他是基於標記-整理算法進行清除的,所以將會產生內存碎片化的問題,這將給大對象分配帶來麻煩。

 

 

成熟的收集器:

 

G1:G1收集器是當今收集器技術發展最前沿的成果,他是一款面向服務端應用的收集器。

 

過程如下:

 

初始標記:標記一下GC root能直接關聯到的對象。,需要停頓線程,耗時短。

    併發標記:從gc root開始對堆中的對象進行分析,找出存活的對象,耗時長,但是可以和用戶線程併發執行。

    最終標記:爲了修正在併發標記過程中因用戶程序繼續運作而導致標記產生變動的那一部分標記記錄。這個階段需要停頓用戶線程。

    篩選回收:首先對各個region的回收價值和成本進行排序,根據用戶所期望的停頓時間來指定回收計劃。

優勢:

 

並行與併發:G1能夠充分利用CPU,多核環境下硬件的優勢,使用多個CPU來縮短stop-the-world停頓時間。

    分代收集:與其他收集器一樣,分代概念在G1仍然保存。

    空間整合:與cms的“標記-清除算法”不同,G1整體上看來是基於“標記-整理的算法”來實現的收集器,這意味着在G1運行期間不會產生內存空間碎片。。

    可預測停頓:這是G1相對於CMS的一大優勢,降低低停頓時間是cms和G1共同的關注點,但是G1還能建立可預測的停頓時間模型,讓使用者明確指定在一個時間爲M的時間片段內,消耗在垃圾收集的時間不得超過N毫秒。

 

垃圾收集器參數總結:

 

 

 

userSerialGC:虛擬機運行在client模式下的默認值,打開此開關之後,使用serial+serial old組合收集器。

 

userParNewGC:打開此開關之後,使用parNew+serial Old組合收集器。

 

userConcMarkSweepGC:打開此開關之後,使用parNew+CMS+serial Old組合垃圾收集器,serial old作爲後備收集器使用。(concurrent mode failure)

 

userParallelGC:server模式下的默認值,parallel Scavenge+serial old組合垃圾收集

 

userParallelOldGC:parallel scavenge+parallel old收集器組合收集。

 

SurvivalRadio:eden:survival= 8

 

userAdaptiveSizePolicy:GC自適應調節策略,調整Java堆各區域的大小以及進入老年代的年齡。

 

handlePromotionFailure:是否允許分配擔保失敗

 

ParallelGCThreads:設置並行GC時進行內存回收的線程數。

---------------------

作者:weixin_37766296

來源:CSDN

原文:https://blog.csdn.net/weixin_37766296/article/details/83585825

版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章