垃圾回收 (電腦科學)

本頁使用了標題或全文手工轉換
維基百科,自由的百科全書

在電腦科學中,垃圾回收(英語:Garbage Collection,縮寫為GC)是指一種自動的記憶體管理機制。當某個程式占用的一部分記憶體空間不再被這個程式訪問時,這個程式會藉助垃圾回收演算法向作業系統歸還這部分記憶體空間。垃圾回收器可以減輕程式員的負擔,也減少程式中的錯誤。垃圾回收最早起源於LISP語言。[1][2]目前許多語言如SmalltalkJavaC#GoD語言都支援垃圾回收器。

原理

垃圾回收器有兩個基本的原理:

  1. 考慮某個物件在未來的程式執行中,將不會被存取。
  2. 回收這些物件所占用的記憶體。[3]

分類

收集器實現

參照計數收集器

最早的也是最簡單的垃圾回收實現方法,這種方法為占用物理空間的對象附加一個計數器,當有其他對象參照這個對象時計數器加一,反之參照解除時減一。這種演算法會定期檢查尚未被回收的對象的計數器,為零的話則回收其所占物理空間,因為此時的對象已經無法訪問。這種方法無法回收迴圈參照的儲存對象。

跟蹤收集器

近現代的垃圾回收實現方法,這種演算法會定期遍歷它管理的記憶體空間,從若干根儲存對象開始尋找與之相關的儲存對象,然後標記其餘的沒有關聯的儲存對象,最後回收這些沒有關聯的儲存對象占用的記憶體空間。

回收演算法

基於其標記和回收行為,又分為若干細緻方法。

標記-清除

先暫停整個程式的全部執行執行緒,讓回收執行緒以單執行緒進行掃描標記,並進行直接清除回收,然後回收完成後,恢復執行執行緒。這樣會產生大量的空閒空間碎片,和使大容量對象不容易獲得連續的記憶體空間,而造成空間浪費。

標記-壓縮

和「標記-清除」相似,不同的是,回收期間同時會將保留的儲存對象搬運匯集到連續的記憶體空間,從而整合空閒空間,避免記憶體碎片化。

複製

需要程式將所擁有的記憶體空間分成兩個部分。程式執行所需的儲存對象先儲存在其中一個分割區(定義為「分割區0」)。同樣暫停整個程式的全部執行執行緒,進行標記後,回收期間將保留的儲存對象搬運匯集到另一個分割區(定義為「分割區1」),完成回收,程式在本次回收後將接下來產生的儲存對象會儲存到「分割區1」。在下一次回收時,兩個分割區的角色對調。[3]

這種方式非常簡單,但是因為只有一個「半空間」(semi-space)被用於分配對象,記憶體使用相較於其他演算法是其兩倍。這種技術也叫做「停止並複製」。Cheney演算法是改進的半空間分配器。

增量回收器

需要程式將所擁有的記憶體空間分成若干分割區。程式執行所需的儲存對象會分布在這些分割區中,每次只對其中一個分割區進行回收操作,從而避免暫停所有正在執行的執行緒來進行回收,允許部分執行緒在不影響回收行為下保持執行,並且降低回收時間,增加程式回應速度。

分代

由於「複製」演算法對於存活時間長,大容量的儲存對象需要耗費更多的移動時間,和存在儲存對象的存活時間的差異。需要程式將所擁有的記憶體空間分成若干分割區,並標記為年輕代空間和年老代空間。程式執行所需的儲存對象會先存放在年輕代分割區,年輕代分割區會較為頻密進行較為激進垃圾回收行為,每次回收完成倖存的儲存對象內的壽命計數器加一。當年輕代分割區儲存對象的壽命計數器達到一定閾值或儲存對象的占用空間超過一定閾值時,則被移動到年老代空間,年老代空間會較少執行垃圾回收行為。一般情況下,還有永久代的空間,用於涉及程式整個執行生命周期的對象儲存,例如執行代碼、資料常數等,該空間通常不進行垃圾回收的操作。 通過分代,存活在局限域,小容量,壽命短的儲存對象會被快速回收;存活在全域域,大容量,壽命長的儲存對象就較少被回收行為處理干擾。

現今的GC(如Java.NET)使用分代收集(generation collection),依照物件存活時間的長短使用不同的垃圾收集演算法,以達到最好的收集效能。

以Java為例,由記憶體管理程式管理的堆可以切割成為兩個部分:

  1. Young:
    1. Eden:存放新生物件。
    2. Survivor:存放經過垃圾回收沒有被清除的物件。
    3. semi-Spaces:和Survivor做Copying collection。
  2. Tenured:物件多次回收沒有被清除,則移到該區塊。

JVM對不同的世代使用不同的GC演算法。

  1. Minor collection:
    YOUNG世代使用將Eden還有Survivor內的資料利用semi-space做複製收集(Copying collection),
    並將原本Survivor內經過多次垃圾收集仍然存活的物件移動到Tenured。
  2. Major collection則會進行Minor collection,Tenured世代則進行標記壓縮收集。[4]

實作

GC機制可以是由程式語言本身提供的功能(如Java、C#),也可以是程式語言以外的第三方函式庫。例如貝姆垃圾收集器就是一種可支援C/C++語言的自動記憶體管理工具。

參見

參考文獻

  1. ^ Recursive functions of symbolic expressions and their computation by machine, Part I. Portal.acm.org. [29 March 2009]. 
  2. ^ Recursive functions of symbolic expressions and their computation by machine, Part I. [29 May 2009]. (原始內容存檔於2013-10-04). 
  3. ^ 3.0 3.1 吳, 昊; 季, 振洲. 一种基于半空间的不完全拷贝垃圾回收机制. 哈爾濱工業大學學報. 2011, 43 (11): 60–64 [2020-03-27]. ISSN 0367-6234. (原始內容存檔於2020-05-28). 
  4. ^ 分代-JVM文档. [2020-03-28]. (原始內容存檔於2020-03-28) (英語).