1. 程式人生 > >深入理解JVM(五) -- 垃圾回收算法

深入理解JVM(五) -- 垃圾回收算法

ava 為什麽 mark 技術 遇到的問題 問題 步驟 遇到 同時

  上篇文章我們了解到哪些內存區域和哪些對象可以被回收,這篇文章我們就來了解一下具體的垃圾回收算法的思路,不討論具體的實現。

  一 最基礎算法 標記-清除(Mark-Swap)

  為什麽說他是最基礎的算法,因為這之後的算法思路都是基於此來進行。如同他的名字一樣,回收分為兩個步驟,標記和清除,但是這種方式有兩個缺陷,一是效率問題,標記和清除兩個過程的效率都不高;二是空間問題,回收後可能產生大量的內存碎片,當需要給大對象分配內存空間的時候,有可能會導致提前觸發一次垃圾回收:

  技術分享圖片

  二 復制算法(Copying)

  為了結解決效率問題,一種名為復制的回收算法出現了,它會將新生代的內存分為兩塊相等的區域,一塊成為Eden,另一塊稱之為Survivor,新生對象在Eden區域分配內存,經歷一次垃圾回收之後,將存活的對象復制到Survivor區域,同時清空Eden區域,但是這樣做會導致新生代50%的內存浪費,根據IBM的研究,大多數情況下,新生代的對象有98%是無法存活到下一次垃圾回收的,HotSpot虛擬機會將新生代按照8:1:1的比例分為一個Eden和兩個Survivor區域(s1和s2),道理一樣,新生對象在Eden分配內存區域,但是,為什麽要分配兩個survivor區域呢?答案是為了保證內存的連續性,按照虛擬機的規定,新生的對象經歷過16次垃圾回收後仍舊存活的話,才會將其分配到老年代,一個survivor區域只能滿足一次回收後的內存連續性,survivor總有一個為空的準備接受存活對象,具體過程如下:當觸發垃圾回收時,Eden和s1中的存活對象會被復制到s2中,同時清空Eden和s1,下次再觸發垃圾回收時,將Eden和s2中存活的對象分配到s1中,清空Eden和s2,s1和s2輪流保存存活對象,才能保證存活對象的內存連續性。

  三 標記-整理算法(Mark-Compact)

  當對象的存活效率較高時,復制的效率會很低,所以又引出了這種算法,思路是:標記的過程都一樣,但後續不是對可回收對象進行清理,而是讓所有存活對象向同一端移動,然後直接清理掉端邊界以外的內存:

  技術分享圖片

  以上就是幾種主流算法的實現思路,但是很明顯,某一種算法都不能完全滿足所有內存區間的高效回收,所以,現代的商用虛擬機都是根據不同區域對象的生存周期特征采用分代收集來實現。在Java中,會將堆內存分為新生代和老年代,在新生代中,每次垃圾收集會有大量的對象死去,只有少量存活,復制成本低,就適合采用復制算法;在老年代中,對象存活率高,就必須使用標記-整理算法來進行回收。

  至此,我們了解了Java虛擬機內存回收算法的幾種實現思路以及分別適應什麽場景,下一篇文章我們將會了解這其中的具體實現中遇到的問題以及應該如何解決。

深入理解JVM(五) -- 垃圾回收算法