說到Java,必定繞不開GC,儘管不是Java獨創的,但Java必定是使用GC的表明。GC就是垃圾回收,更直接點說就是內存回收。是對內存進行整理,從而使內存的使用盡量大的被複用。 一直想好好寫一篇關於GC的文章,但是卻發現要寫的東西太大了,不是一篇博客能簡單的介紹完的。因此打算拆分紅若干篇博客,一點點的總結下來。 本篇主要介紹的是GC中的經常使用算法。這些算法被普遍的應用於各個內存管理語言的虛擬機中,或者是各大經常使用的操做系統中。 說到GC,也就是垃圾回收,那麼須要作的事有兩件:html
第一件是查找到哪些內存中的對象是已經廢棄掉的。
第二件事是如何清理掉這些已經廢棄掉的數據。
本文先來講說後者,如何清除掉這些內存中的廢棄數據。算法
一、標記清除算法Mark-Sweep sql
這種算法是最簡單最直接的算法,也是其它算法的一些最初思路。標記清除算法其實就是對內存中的對象依次的進行判斷,若是對象須要回收那麼就打一個標記,若是對象仍然須要使用,那麼就保留下來。這樣通過一次迭代以後,全部的對象都會被篩選判(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )斷一次。緊接着會對內存中已經標記的對
象依次進行清除。 這個算法比較簡單粗暴,實現起來比較簡單。
可是會留下兩個比較麻煩的問題:
(1)標記和清除須要兩遍循環內存中的對象,標記自己也是一個比較麻煩的工做,所以這種算法的效率不是特別的高。
(2)對於分配的內存來講,每每是連續的比較好,由於這樣有利於分配大數據的對象。假若當前內存中都是小段的內存碎片,會知道須要分配大段內存時,沒有能夠放置的位置,而觸發內存回收。也就是空間不足而致使頻繁GC和性能降低。數據庫
二、複製算法Copying 編程
我在使用數據庫的過程當中,曾經遇到這樣一個問題,表中的數據量相對來講比較大,大概30萬行,須要使用多個苛刻的條件刪除其中的大部分數據(所以沒法使用索引),而只保留其中的較少數據。常規的delete語法使用起來是超時的。因而我查看維護人員的sql,發現一個頗有意思的邏輯。首先查出數據庫表中須要保留的數據,放到一張臨時表中。而後完全刪除掉原有的數據表。而後把這張臨時建立表的表名,改成當初的表名。這是一種典型的空間換取時間的方法。而複製算法就是這樣一個思路。 複製算法中,會將內存劃分爲兩塊相等大小的內存區域A/B,而後生成的數據會存放在A區,當A區剩餘空間不足以存放下一個新建立的對象時,系統就會將A區中的有效對象所有複製到B區中,並且是連續存放的。而後直接清空A區中的全部對象。 因爲編程語言中的對象,大部分在建立後很快就(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )會被回收掉,因此咱們須要複製的對象其實並很少。 Java中的實現是這樣的: Java中將Eden和Survivor區同時做爲複製算法的使用區域。Survivor又分爲From區和To區。這塊內容能夠參考個人另一篇博客,博客中有詳細的介紹:http://www.cnblogs.com/jilodream/p/6147791.html。每次GC的時候都會將Eden和Survivor的From區中的有效對象進行標記,一同複製到Survivor的To區。而後完全清除原來的Eden區和From區的內存對象。與此同時To區就是下一次回收的From區。編程語言
複製算法的缺點: 算法使用了空間換取時間的思路,所以須要一塊空白的區域做爲內存對象要粘貼的區域。這無疑會形成一種浪費。尤爲是內存較小時。 算法每次清除無效對象時,都要進行一次複製粘貼的對象轉移,所以對使用場景是有限制的。只有在有效對象佔據總回收內存是很是小的時候,這種算法的性價比纔會達到最高。不然大量的複製動做所浪費的時間可能要遠遠大於空間換取時間獲得的收益。所以這種算法在Jvm中,也只被用來做爲初級的對象回收。由於這時的有效對象比例最低,算法的性價比是最高的。性能
三、 標記整理算法 Mark-Compact大數據
複製算法須要一塊額外的內存空間,用於存放倖存的內存對象。這無疑形成了內存的浪費。咱們還能夠在原有的標記清除算法的基礎上,提出了優化方案。也就是標記到的可用對象總體向一側移動,而後直接清除掉可用對象邊界意外的內存。(防盜鏈接:本文首發自http://www.cnblogs.com/jilodream/ )這樣既解決了內存碎片的問題。又不須要原有的空間換時間的硬件浪費。因爲老年代中的倖存對象較多,並且對象內存佔用較大。這就使得一旦出現內存回收,須要被回收的對象並很少,碎片也就相對的比較少。因此不須要太多的複製和移動步驟。所以這種方法經常被應用到老年代中。 優化
標記整理算法的缺點: 標記整理算法因爲須要不斷的移動對象到另一側,而這種不斷的移動實際上是很是不適合雜而多的小內存對象的。每次的移動和計算都是很是複雜的過程。所以在使用場景上,就註定限制了標記整理算法的使用不太適合頻繁建立和回收對象的內存中。spa
四、分代收集算法 Generational Collection
這種算法就是將內存以代的形式劃分,而後針對狀況分別使用性價比最高的算法進行處理。在Java中,通常將堆分爲老年代和新生代。新建立的對象每每被放置在新生代中。而通過不斷的回收,逐漸存活下來的對象被安置到了老年代中。越新的對象越可能被回收,越老的對象反而會存活的越久。所以針對這兩種場景,新生代和老年代也會分別採用前文所述的兩種算法進行清理。
ps:話說如今寫篇博客愈來愈難了,今天覺得畫個圖就能夠發佈了,結果畫圖畫了一個小時。哎