垃圾收集算法

Java堆是垃圾收集器管理的主要區域

Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱爲是”GC”堆。從內存分配的角度看,線程共享的Java堆可能劃分出多個線程私有的分配緩衝區(Thread Local Allocation Buffer,TLAB)。從內存回收的角度來看,由於現在收集器基本都採用分代收集,所以Java堆還可以細分爲:新生代和老年代;再細緻一點新生代可以分爲Eden空間、From Survivor空間、To Survivor空間等。不過無論如何劃分,都與存放的內容無關,無論哪個區域,存放的都仍然是對象實例。進一步劃分的目的是爲了更好地回收和分配內存。

新生代採用複製的方式收集內存,將內存分爲一塊較大的Eden區和兩個較小的Survivor區。新建對象總是在Eden區,當Eden區已滿,就觸發一次young GC,將還存活的對象複製到From Survivor空間。這樣Eden區都是未被使用的空間,可供繼續創建對象,當Eden區再次被使用滿,就會觸發一次young GC,當回收時,將Eden區和Survivor區中還存活的對象一次性地複製到另外一個Survivor區上。後面就是每次使用Eden區和一個survivor區,當回收時,將Eden區和Survivor區中還存活的對象一次性地複製到另外一個Survivor區上。如果超過某個閾值對象還未被釋放,則將該對象複製到老年代。

Eden區存新分配內存的對象,Survivor區存至少經歷了一次垃圾回收,並得以倖存的對象。老年代則存放年長的對象(存在時間較長,經過垃圾回收次數較多的對象)。持久代(方法區)。

標記-清除算法

首先標記出所有需要回收的對象,在標記完成後統一回收所有被標記的對象。

主要的不足:① 效率問題:標記和清除的效率都不高;②空間問題:標記清除後,空間內會有大量不連續的內存碎片,空間碎片太多會導致以後在程序運行過程中需要分配較大對象時,無法找到足夠大的內存而不得不提前觸發一次垃圾回收操作。

複製算法

將可用的內存按容量大小分爲兩塊大小相等的兩塊,每次只使用其中的一塊。當某一塊的內存使用完了,就把還存活的對象移到另一塊內存上,然後把當前這塊做清理掉。

優點:實現簡單,運行高效。

缺點:代價太大,將原來的內存縮小爲原來的一半。

標記-整理算法

標記過程與”標記-清除算法“一樣,但是後續步驟不是直接對可回收的對象進行清理,而是把所有存活的對象都移到一端,然後直接清理掉端邊界以外的內存。

分代收集算法

當前商業虛擬機的垃圾收集都採用”分代收集”(Generational Collection),根據對象存活週期的不同將內存劃分爲幾塊。一般是把堆分爲新生代和老年代,在新生代採用複製算法回收內存,老年代採用標記-整理或標記-清除算法。算法在運行的過程中優先收集處於新生代的對象,如果一個對象經過多次收集還存活,那麼就可以把這個對象移到高一級的堆裏,減少對其掃描的次數。

JVM中GC停頓是爲什麼?

判斷對象是否存活的可達性分析對時間的敏感還體現在GC停頓上,因爲可達性分析工作必須在一個能確保一致性的快照中進行,這裏的“一致性”的意思是指在整個分析期間整個執行系統看起來就像被凍結在某個時間點上,不可以出現分析過程中對象引用關係還在不斷變化的情況。不然的話可達性分析的結果就無法得到保證,這是導致GC進行時必須停頓所有Java執行線程的一個重要原因。

垃圾回收是什麼

概念:在Java語言中,垃圾回收(Garbage Collection,GC)是一個非常重要的概念,它的主要作用是回收程序中不再被使用的內存,Java提供的GC功能可以自動監測對象是否已經超過作用域從而達到自動回收內存的目的。即垃圾回收的是無任何引用的對象佔據的內存空間而不是對象本身。

任務:Java語言提供了垃圾回收器來自動檢測對象的作用域,可自動地把不再被使用的存儲空間釋放掉。具體而言,垃圾回收器主要負責三個事情①分配內存;②保證不再被使用的內存被釋放掉;③保證被引用的對象不會被回收。

 方式:垃圾回收器通常是作爲一個單獨的低優先級的線程運行,不可預知的情況下對內存堆中已經死亡或者長時間沒有使用的對象進行清除和回收,程序員不能實時的調用垃圾回收器對某個對象或所有對象進行垃圾回收。

垃圾回收的兩面性

好的一面:垃圾回收器的存在一方面把開發人員從釋放內存的複雜工作中解脫出來,提高了開發人員的生產效率;另一方面,對開發人員屏蔽了釋放內存的方法,可以避免因開發人員錯誤地操作內存而導致應用程序的崩潰,保證了程序的穩定性。

不好的一面:但是垃圾回收也帶來了問題,爲了實現垃圾回收,垃圾回收器必須跟蹤內存的使用情況,釋放沒用的對象,在完成內存的釋放後還需要處理堆中的碎片,這些操作必定會增加JVM的負擔,從而降低程序的執行效率。

爲什麼要有垃圾回收

在程序開發過程中如果忘記或者錯誤的釋放內存,往往會導致程序運行不正常甚至是導致程序崩潰。爲了減輕程序開發人員的工作和保證程序的安全與穩定,java語言提供了垃圾回收器來自動檢測對象的作用域,可自動地回收不可能再被引用的對象所佔用的內存。

要請求垃圾收集,可以調用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉顯式的垃圾回收調用。

什麼時候運行垃圾回收

垃圾回收可能發生的時間是堆可用空間不足或CPU空閒。

對於HotSpot虛擬機,用OopMap數據結構存儲程序中哪些地方存放着對象引用的信息。程序執行時並不是在所有地方都能停頓下來開始GC,只有到達安全點時才能暫停。其中安全點指的是記錄OopMap數據結構的位置。

安全點的選擇基本上是以程序“是否能讓程序長時間執行的特徵”爲標準進行選定的。“長時間執行”的最明顯的特徵就是指令序列複用,例如方法調用、循環跳轉、異常跳轉等,所有具有這些功能的指令纔會產生safepoint。對於safepoint,另一個需要考慮的問題就是如何在GC發生時讓所有線程都“跑”到最近的安全點上再停頓下來。有兩種方案可以選擇:搶佔式中斷和主動式中斷。

當線程處於sleep狀態或者blocked狀態時,這時候線程可能無法響應JVM的中斷請求,“走到”安全的地方中斷掛起。對於這種情況,就需要安全區域來解決。安全區域是指在一段代碼片段之中,引用關係不會發生變化,在這個區域的任何地方開始GC都是安全的。因此也可以把安全區域看做被擴展了的安全點。在線程執行到Safe Region中的代碼時,首先標識自己已經進入到了Safe Region,那樣,當在這段時間裏JVM要發起GC時,就不用管標識自己爲Safe Region狀態的線程了。在線程要離開Safe Region時,要檢查系統是否已經完成了根結點的枚舉(或者整個GC過程),如果完成了,那線程就繼續執行,否則它就繼續等待直到收到可以安全離開Safe Region的信號爲止。

總之,程序並非是時時刻刻都可以去執行GC操作,只有程序運行到安全區域的時候纔可以發起GC的操作。

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