1. 程式人生 > >關於JAVA回收機制

關於JAVA回收機制

使用JAVA程式設計時,幾乎不需要考慮“記憶體洩漏”的問題,這也是JAVA相較於C++的一個優點。

最近在看《Java程式設計思想》(第四版,聽說第五版有點牛逼。。。。),裡面講到JAVA的回收機制,在這裡記錄一下。

書中首先說到的是引用計數:

  這是一種很“簡單”,但是速度很慢的垃圾回收技術。這個機制就是說每一個物件都含有一個"引用"計數器,當有”引用“連線到這個物件的時候,計數器就會+1;當”引用“離開作用域,或者被置為null時,計數器就會-1。雖然這個計數器可能不會佔用太多的資源(空間),但是這個過程貫穿了程式的真個生命過程。 垃圾回收器會在含有”全部物件“的列表上進行遍歷,當發現某一個計數器是0的時候,就釋放這個物件佔用的空間。這個方法有一些缺陷,當A物件中引用了B物件,A物件的引用消失了,但是此時,程式並不會認為B物件引用也減少了,因而會產生一些”錯誤“,該被釋放掉的沒有釋放。這種方式也從來沒有被任何一種JAVA虛擬機器實現。

然後是停止-複製:

  在這個模式認為,任何“活著”的物件,都一定能追溯到它存在於堆疊或者靜態儲存區的引用。這種模式下,程式會先暫停執行,然後將所有“活著”的物件從當前的堆中,複製到另一個堆。這個時候沒有被複制並且放進新堆中的物件,都會被認為是垃圾。在新堆中,這些“活著”的物件是一個挨著一個的,位置緊湊。

  這種方式的效率相對比較低,原因有兩個:

  1. 這種複製的方式,需要兩個堆,從一個堆複製到另一個堆去,需要的空間就是實際所需空間的一倍了。(有一些JAVA虛擬機器在處理這個問題時,會將堆劃分成幾塊較大的記憶體,複製動作發生在這幾個大塊之間)
  2. 複製,也是一個問題。當程式穩定執行的時候,垃圾應該是很少的,或者說沒有垃圾,但是此時,依然要執行復制,來保證清楚垃圾,不浪費資源。而複製本身就在浪費資源。

最後是標記-清除:

  這個模式的思路就是從堆疊和靜態儲存區出發,遍歷所有的引用,可以找到“活著”的物件。找到“活著”的物件的時候,就會給這些“活著”的物件一個標記,此時暫時不在執行其他動作。當標記工作全部完成之後,清理才會開始。那些沒有獲得“活著”標記的物件將會被釋放。但是這樣做,卻會造成記憶體空間的不緊湊,可能記憶體空間因為一些物件的釋放變得不再連續。這個模式也是在程式暫停時才執行的。

 

  就停止-複製而言,垃圾回收器在回收那些無用物件之前,必須要先將活著的物件拷貝到新的堆中。這裡假設記憶體被分成了多個較大的”塊“。則複製行為發生在這些”塊“之間,一些大物件會獨自佔有一個”塊“,對於這些大型物件,他們不會被複制,而是他所在的塊代數會增加,對於那些小型物件,就使用複製並且釋放的方法。當大多數物件都趨於穩定的時候,垃圾回收器還使用這種模式就顯的效率很低下,所以這個時候就會切換到標記-清除

模式;同樣,JAVA虛擬機器這個時候也會監控標記-清除模式工作效果,當出現很多破碎的空間時,就切換到停止-複製模式。

  書中稱到:這是一種自適應分代的停止-複製標記-清除 式垃圾回收器