記憶體回收行程是如何工作的?我現在就簡單的介紹一下
首先要明確幾點:
Java是在堆上為對象分配空間的
記憶體回收行程只跟記憶體有關,什麼IO啊,網路連接啊,管它P事
當可用記憶體數量較低時,Sun版本的記憶體回收行程才會被啟用
在記憶體回收行程回收垃圾之前,我們先來瞭解一下Java指派至的方式,Java的堆更像一個傳送帶,每分配一個新對象,它就往前移動一格。這意味著Object Storage Service空間的分配速度相當快。Java的“堆積指標”只是簡單地移動到尚未分配的領域。也就是說,分配空間的時候,“堆積指標”只管依次往前移動而不管後面的對象是否還要被釋放掉。如果可用記憶體耗盡之前程式就退出就再好不過了,這樣的話記憶體回收行程壓根就不會被啟用。
但是由於“堆積指標”只管依次往前移動,那麼你肯定會想,總有一天記憶體會被耗盡,記憶體回收行程就開始釋放記憶體。這裡有人肯定會問:怎麼判斷某個對象該被回收呢?答案就是當堆棧或靜態儲存區沒有對這個對象的引用時,就表示程式(員)對這個對象沒有興趣了,它就應該被回收了。有兩種方法來知道這個對象有沒有被引用:第一種是遍曆堆上的對象找引用;第二種是遍曆堆棧或靜態儲存區的引用找對象。前者的實現叫做“引用計數法”,意思就是當有引用串連至對象時,引用計數加1,當引用離開範圍或被置為null時,引用計數減1,這種方法有個缺陷,如果對象之間存在循環參考,可能會出現“對象應該被回收,但引用計數卻不為零”的情況。
Java採用的是後者,在這種方式下,Java虛擬機器採用一種“自適應”的記憶體回收技術,如何處理找到的存活對象(也就是說不是垃圾),Java有兩種方式:
一種是“停止-複製”:理論上是先暫停程式的運行(所以它不屬於後台回收模式),然後將所有存活的對象從當前堆複製到另一個堆,沒有被複製的全是垃圾。當對象被複製到新堆上時,它們是一個挨著一個的,所以新堆保持緊湊排列(這也是為什麼指派至的時候“堆積指標”只管依次往前移動)。然後就可以按前述方法簡單、直接地分配記憶體了。這將導致大量記憶體複製行為,記憶體配置是以較大的“塊”為單位的。有了塊之後,記憶體回收行程就可以不往堆裡拷貝對象了,直接就可以往廢棄的塊裡拷貝對象了。
另一種是“標記-清掃”:它的思路同樣是從堆棧和靜態儲存區出發,遍曆所有的引用,進而找出所有存活的對象。每當它找到一個存活對象,就會給對象一個標記。這個過程中不會回收任何對象。只有全部標記完成時,沒有標記的對象將被釋放,不會發生任何複製工作,所以剩下的堆空間是不連續的,然後記憶體回收行程重新整理剩餘的對象,使它們是連續排列的。
當記憶體回收行程第一次啟動時,它執行的是“停止-複製”,因為這個時刻記憶體有太多的垃圾。然後Java虛擬機器會進行監視,如果所有對象都很穩定,記憶體回收行程的效率降低的話,就切換到“標記-清掃”方式;同樣,Java虛擬機器會跟蹤“標記-清掃”效果,要是堆空間出現很多片段,就會切換到“停止-複製”方式。這就是所謂的“自適應”技術。
其實仔細想一下,“停止-複製”和“標記-清掃”無非就是:“在大量的垃圾中找乾淨的東西和在大量乾淨的東西裡找垃圾”。不同的環境用不同的方式,這樣做完全是為了提高效率,要知道,無論哪種方式,Java都會先暫停程式的運行,所以,記憶體回收行程的效率其實是很低的。Java用效率換回了C++沒有的記憶體回收行程和運行時的靈活,我認為這是明智的選擇(雖然它只跟記憶體有關),隨著硬體的飛速發展,我相信,開發時間要比運行效率重要得多!
引用:http://fhz1980.blog.163.com/blog/