在Java中,除了整數和引用這樣的基本類型,所有物件都被分配在堆疊區而不是堆疊區。這種設計使得程式設計師不需要專注於變數的生命週期,但代價是產生更多的垃圾。
可達性
對任何指標解引用就可以被程式直接存取的資料則為可達的。
局部性原理
如果一個程式方位的儲存位置很可能將在一個很短的時間段再次被訪問,則稱這個程式具有時間局部性(Temporal locality)。如果被訪問過的儲存位置的臨近位置很可能在一個很短的時間段內被訪問,則該程式具有空間局部性。
通常認為程式將90%的時間來執行10%的程式碼。
幾種垃圾回收器的原理
標記-清除收集器
#這種收集器首先遍歷對象圖並標記可到達的對象,然後掃描堆疊以尋找未標記物件並釋放它們的記憶體。這種收集器一般使用單線程工作並停止其他操作。並且,由於它只是清除了那些未標記的對象,而並沒有對標記對象進行壓縮,導致會產生大量內存碎片,從而浪費內存。
標記-壓縮收集器
有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,則把標記物件複製到堆疊的新域中以便壓縮堆疊。這種收集器也停止其他操作。
複製收集器
這種收集器將堆疊分成兩個域,常稱為半空間。每次僅使用一半的空間,JVM產生的新物件則放在另一半空間。 GC運作時,它把可到達物件複製到另一半空間,從而壓縮了堆疊。這種方法適用於短生存期的對象,持續複製長生存期的對象則導致效率降低。並且對於指定大小堆來說,需要兩倍大小的內存,因為任何時候都只使用其中的一半。
增量收集器
增量收集器把堆疊分成多個域,每次只從一個域收集垃圾,也可理解為把堆疊分成一小塊,每次僅對某一個區塊進行垃圾收集。這會造成較小的應用程式中斷時間,使得使用者一般不能覺察到垃圾收集器正在運作。
部分回收原理
通常80%~90%的新分配物件在數百萬條指令之內或再分配了。
分代收集器(Generational garbage coolection)
它是基於拷貝回收器和部分回收原理。
充分利用大多數物件「英年早逝」的特性的有效方法。
將堆區分成一系列小的區域,用0,1,2......n對它們進行編號,序號越小的區域存放的對象越年輕,對象首先在0區域被創建,填滿後垃圾被回收,可達物件被移到1區,每一輪垃圾回收都是針對序號小於等於i的區域進行的,i為當前被填滿區域的最高編號。
只要對i進行回收,所有序號小雨i的區域也將要進行垃圾回收,應為較年輕的世代往往包含了較多的垃圾,也就是更頻繁的被回收。
最老的世代保存了最成熟的對象,對這些對象的回收是最昂貴的,相當於一次完整的回收。可引起較長時間的停頓。解決方法是使用列車演算法。
HotSpot的四種GC 回收器:
串行化回收serial collector:
特點:回收時會暫停應用.
young區域:將Eden和某個Survivor區域中的存活的物件複製到另一個Survivor區域(設為TO)(大物件直接放到old區域).如果TO區域滿了,則直接複製到old區域.
old區域:使用mark-sweep-compact GC演算法,也就是先標記存活物件,然後清除廢棄物件,然後把存活物件都移到一塊區域,空出一片較大的空閒空間.
適用範圍:大部分客戶端的應用都可以使用這種回收演算法,這也是HotSpot預設的回收演算法.在現在的機器(06年)上一個64MB的區域的一次完全回收所需的時間不到半秒鐘.
並行回收parallel collector :
特點:可以利用多核心的CPU.
young區域:同樣還是要暫停應用,基本機制和串行化差不多,不過是使用多線程.可以加快效率.
old區域:同串行化.
多核心計算機上面可以使用.
並行壓縮回收parallel compacting collector:
與並行回收相比,主要是在old區域有個新的算法,同時,按白皮書的說法,這種回收最終會替代並行回收.
young區域:同並行回收.
old區域:首先,把old分成幾個連續的區域.然後,在每個區域並行的進行檢查,標記出alive的對象(先標記出可以直接引用的對象,然後是所有的).然後開始對這些區域進行檢查,得出密集程度(左邊的區域肯定比右邊的密集),從某個密集程度不很高的區域開始,並行的對右邊區域進行壓縮.
適應範圍:對於多核心,且對pause time有要求的環境下,使用並行壓縮回收比並行回收要好.但是對於高共享率的伺服器(也就說一台伺服器運行多個應用),由於old區域的collection較慢,又是多線程,所以一個應用的GC會對其他應用造成影響.對應的解決方法:可以配置減少並行時的線程數目.
並行標記清除回收Concurrent Mark Sweep collector :
young區域:同並行回收.
old區域:分為幾個步驟.
Initialmark:在需要執行GC時,先暫停應用,然後把所有直接引用到的對象進行標記.
Concurrentmark:然後繼續應用,並同時對已標記對象進行檢查,得到所有存活的對象.
remark:再次暫停應用,對Concurrent mark持續期間應用程序修改了的對象進行檢查(新增的,廢棄的),並標記存活對象.這個階段持續時間較長,因此會使用多線程.在階段結束後,所有的存活對像都被標記了,未標記的對象就是垃圾對象了.
sweep:停止暫停應用程式,然後把所有垃圾物件的空間釋放.
與其他演算法的不同點:
第一:不執行壓縮.不過會透過計算將來可能的記憶體需求而合併/分割某些記憶體區塊.
第二:不是old區域要滿了才執行GC,而是在空間小於一定程度時開始.
第三:由於沒執行壓縮,因此會產生碎片.
另外,CMS還可以使用增量運作方式,就是在Concurrentmark階段只執行一部分工作,然後把資源還給應用程式.回收器的工作會分為幾個部分並安排在兩次young區域的回收空閒階段完成.這種模式一般用在對暫停時間有要求,同時處理器數目不多的情況下(單核或雙核).
總體說來,與並行回收相比,CMS降低了old GC的暫停時間(有時候效果很顯著),輕微的加長了young GC的時間(因為對象從young區域轉到old區域時間會加長:沒執行壓縮,因此要先找到合適的區域),降低了整個系統的一些執行效率,以及很大的加強了對於記憶體空間的需求.
以上是詳解Java中的幾種垃圾回收原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!