垃圾回收器的原理

垃圾回收器是如何工作的?我現在就簡單的介紹一下

首先要明確幾點:

Java是在堆上爲對象分配空間的

垃圾回收器只跟內存有關,什麼IO啊,網絡連接啊,管它P事

當可用內存數量較低時,Sun版本的垃圾回收器纔會被激活

在垃圾回收器回收垃圾之前,我們先來了解一下Java分配對象的方式,Java的堆更像一個傳送帶,每分配一個新對象,它就往前移動一格。這意味着對象存儲空間的分配速度相當快。Java的“堆指針”只是簡單地移動到尚未分配的領域。也就是說,分配空間的時候,“堆指針”只管依次往前移動而不管後面的對象是否還要被釋放掉。如果可用內存耗盡之前程序就退出就再好不過了,這樣的話垃圾回收器壓根就不會被激活。

但是由於“堆指針”只管依次往前移動,那麼你肯定會想,總有一天內存會被耗盡,垃圾回收器就開始釋放內存。這裏有人肯定會問:怎麼判斷某個對象該被回收呢?答案就是當堆棧或靜態存儲區沒有對這個對象的引用時,就表示程序(員)對這個對象沒有興趣了,它就應該被回收了。有兩種方法來知道這個對象有沒有被引用:第一種是遍歷堆上的對象找引用;第二種是遍歷堆棧或靜態存儲區的引用找對象。前者的實現叫做“引用計數法”,意思就是當有引用連接至對象時,引用計數加1,當引用離開作用域或被置爲null時,引用計數減1,這種方法有個缺陷,如果對象之間存在循環引用,可能會出現“對象應該被回收,但引用計數卻不爲零”的情況。

Java採用的是後者,在這種方式下,Java虛擬機採用一種“自適應”的垃圾回收技術,如何處理找到的存活對象(也就是說不是垃圾),Java有兩種方式:

一種是“停止-複製”:理論上是先暫停程序的運行(所以它不屬於後臺回收模式),然後將所有存活的對象從當前堆複製到另一個堆,沒有被複制的全是垃圾。當對象被複制到新堆上時,它們是一個挨着一個的,所以新堆保持緊湊排列(這也是爲什麼分配對象的時候“堆指針”只管依次往前移動)。然後就可以按前述方法簡單、直接地分配內存了。這將導致大量內存複製行爲,內存分配是以較大的“塊”爲單位的。有了塊之後,垃圾回收器就可以不往堆裏拷貝對象了,直接就可以往廢棄的塊裏拷貝對象了。

另一種是“標記-清掃”:它的思路同樣是從堆棧和靜態存儲區出發,遍歷所有的引用,進而找出所有存活的對象。每當它找到一個存活對象,就會給對象一個標記。這個過程中不會回收任何對象。只有全部標記完成時,沒有標記的對象將被釋放,不會發生任何複製工作,所以剩下的堆空間是不連續的,然後垃圾回收器重新整理剩餘的對象,使它們是連續排列的。

當垃圾回收器第一次啓動時,它執行的是“停止-複製”,因爲這個時刻內存有太多的垃圾。然後Java虛擬機會進行監視,如果所有對象都很穩定,垃圾回收器的效率降低的話,就切換到“標記-清掃”方式;同樣,Java虛擬機會跟蹤“標記-清掃”效果,要是堆空間出現很多碎片,就會切換到“停止-複製”方式。這就是所謂的“自適應”技術。

其實仔細想一下,“停止-複製”和“標記-清掃”無非就是:“在大量的垃圾中找乾淨的東西和在大量乾淨的東西里找垃圾”。不同的環境用不同的方式,這樣做完全是爲了提高效率,要知道,無論哪種方式,Java都會先暫停程序的運行,所以,垃圾回收器的效率其實是很低的。Java用效率換回了C++沒有的垃圾回收器和運行時的靈活,我認爲這是明智的選擇(雖然它只跟內存有關),隨着硬件的飛速發展,我相信,開發時間要比運行效率重要得多

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