垃圾收集概念
垃圾收集器負責以下幾個事情:
1.分配內存 2.確保被引用的對象留在內存中3.回收執行中代碼的引用無法到達的對象佔用的內存(by zhangxsh:強調執行中是為了排除對象互相引用的情況。A、B互相引用,但沒有任何執行中代碼引用他們,A、B也應被回收)
對象被引用稱為活著的(live)。不再被引用的對象稱為死掉的(dead),術語叫垃圾(garbage)。發現和釋放(或者叫回收(reclaiming))這些垃圾佔用的空間叫做垃圾收集(garbage collection)。
垃圾收集可以解決很多內存分配問題,卻不能解決所有的內存分配問題。例如,你可以創建對象並無限期的引用著直到沒有內存可用。垃圾收集在使用其自身的時間和資源上是一個複雜的任務。
垃圾收集器負責的內存是由精確的演算法來組織、分配和回收的,並對開發人員隱藏。空間通常從一個被稱為堆(heap)的非常大的內存池分配出來。垃圾收集的時間由垃圾收集器決定。通常,整個堆或堆的一部分被填滿或者達到某個閾值的時候,會觸發垃圾收集。
滿足內存分配的請求是一個困難的任務,這其中包括要從堆中找到一個足夠大的內存塊。對於大部分的動態內存分配演算法,其主要的問題是需要在保持內存分配和回收效率的同時避免內存碎片。
1.優秀的垃圾收集器的特徵
垃圾收集器必須是安全有效的。就是說,使用中的數據永遠不能被錯誤的釋放,同時在很少的幾個收集周期內垃圾就應該被回收。
垃圾收集器的執行效率也必須很優秀,暫停時間不能太長。暫停的時候,應用系統是不運行的。然而,像大部分的計算機系統那樣,這裡也必須在時間、空間、頻率之間做出權衡。例如,堆很小的時候,垃圾收集的速度很快但堆被填滿的速度更快,這樣就需要更頻繁的垃圾收集。相反的,大的堆填滿的速度慢,收集的頻率也慢,但花費的時間會比較長。
另一個特徵是有限的內存碎片(fragmentation)。當垃圾對象的內存釋放後,釋放的空間會在各種各樣的區域形成小塊的空隙以至於可能導致沒有一個足夠大的連續區域分配給較大的對象。一種減少內存碎片的方法叫做壓縮(compaction),在下面垃圾收集器的設計決策部分會討論到。
可擴展性同樣很重要。在多核系統上運行的多線程程序中,內存分配、垃圾收集都不能成為瓶頸。
2.設計決策
設計和選擇垃圾收集演算法時必須做出一系列選擇:
串列還是並行
在串列收集中,同一時間只做一件事情。例如,即便有多個cpu可用,卻只有一個進行收集工作。當使用並行收集時,垃圾收集任務會分成幾個小的部分,這些小的部分在不同的cpu上同時執行。同時執行的操作使得收集速度更快,它的代價是額外的複雜性和可能更多的內存碎片。
並發的(Concurrent)還是停止一切(Stop-the-world)
當執行停止一切(Stop-the-world)的垃圾收集時,應用系統在收集期間完全暫停(suspended)了。另外一種選擇是,一個或多個垃圾收集任務可以和應用系統同時並發的執行。通常,一個並發的收集器,大部分工作並發的執行,但仍會有一些短暫的暫停(stop-the-world pauses)。停止一切的垃圾收集比並發收集更簡單,因為整個堆都凍結了,在收集期間對象不會改變。它缺點是一些應用程序不喜歡的暫停(paused)。相應的,並發收集的暫停時間更短,但收集器必須格外的小心,執行收集的同時應用系統可能會改變對象的狀態,這會增加一些開銷。並發收集會影響性能並且需要較大的堆內存。
壓縮or不壓縮or拷貝
當收集器判定內存中的對象哪些是存活的哪些是垃圾之後,收集器可以壓縮(compact)內存,將所有存活的對象放到一起,從而完全的恢復剩餘的內存。壓縮之後,在第一個空閑位置分配內存將會非常的容易和迅速。可以用一個簡單的指針維持下一個可分配對象的位置。相對壓縮的收集器,非壓縮(non-compacting)的收集器在原地(in-place)釋放垃圾對象佔用的空間,它不會像壓縮的收集器那樣移動存活的對象創建一個大的回收區。非壓縮的好處是收集完成的很快,缺點是可能有內存碎片。一般來說,從原地釋放的內存分配空間比從壓縮的堆分配內存更困難些。它必須搜素堆空間找到一個足夠大能容納新對象的連續內存區域。第三種可供選擇的是複製(copying)收集器,拷貝(或疏導evacuates)所有活動的對象到另一個不同的內存區域。它的好處是原來的區域可以直接置空,簡單快速的為隨後的內存分配做好準備,缺點是需要額外的空間和時間。
3.性能度量
一些指標用來評估垃圾收集的性能,包括:
吞吐量(Throughput)
一個很長的周期中,除去花費在垃圾收集上的時間佔總時間的百分比。
垃圾收集開銷(Garbage collection overhead)
與吞吐量相反,這是垃圾收集佔總時間的百分比。by zhangxsh:為什麼需要兩個指標呢?對於並發的垃圾收集演算法,垃圾收集的部分任務和應用系統同時運行導致上述兩個指標加起來會大於100%)
暫停時間(Pausetime)
垃圾收集發生時,應用系統暫停的時間。
收集頻率(Frequencyof collection)
垃圾收集相對於應用系統運行發生的頻率。
佔用空間(Footprint)
一種大小的指標,例如堆大小。
反應時間(Promptness)
對象變成垃圾之後到內存可用的時間一個互動式應用需要較低的暫停時間,反之持續的執行時間對於非互動式應用更加重要。一個實時應用程序要求在垃圾收集中的暫停以及收集器的整個周期擁有較少的抖動。運行在個人計算機或嵌入式設備中的應用可能主要關心小的空間佔用。
4.分代收集
使用分代(generational collection)收集技術時,內存分為很多代(generations),分離的存儲池存儲不同年齡的對象。例如,最通用的配置中有兩代:一個用於存放年輕的對象,另一存放年老的對象。
不同的代使用不同的演算法執行垃圾收集任務,每個演算法會基於本代獨特的特徵進行優化。分代的垃圾收集基於一種被稱為弱分代假設(weak generational hypothesis),它是關於在幾種語言(包括java語言)編寫的應用程序中觀察到的結果:
1、大部分的分配的對象不會被引用(存活)很長時間,這些對象在年輕的時候就死掉了;
2、年老對象引用年輕對象的情況很少出現。
年輕代的收集發生的相對頻繁、有效、快速,因為年輕代的空間通常比較小並且有很多的對象都不再被引用。
在年輕代幾次收集後仍然生存的對象最終會晉陞(promoted)或者被授予(tenured)到年老代。如圖1。年老代一般比年輕代大,並且增長的速度很慢。結果是,年老代的收集很少發生,但是會花費更長的時間才能完成。
為年輕代設計的收集演算法主要關注在速度方面,因為垃圾收集經常發生。另一方面,在空間方面更有效率的演算法管理著年老代,因為年老代佔據了大部分的堆空間並且年老代的垃圾密度比較低。


TAG:技術萬花筒 |