【JVM】垃圾回收算法
Java是一種進階語言,它不需要C語言那樣由程式員手動開辟記憶體,用完之後手動釋放記憶體,Java有自己的垃圾回收器,對于記憶體中的那些不使用的對象,垃圾收集器會自動回收,垃圾回收的時候使用不同的算法,針對不同的場景使用不同的算法,以達到提升回收效率的效果。
在JVM中主要有三種垃圾回收算法,标記清除、标記整理、複制算法。
标記清除(Mark Sweep)
标記清除分為兩個階段,标記和清除。
我們假設記憶體中目前的記憶體配置設定如下:
可以看到綠色和紅色對象由于有GC Root關聯,通過可達性分析可知是不應該被回收的,是以标記他們,但是藍色對象object4和object6是應該被回收的。
标記階段:會從根節點出發周遊對象關系,對通路過的對象打賞标記,标記為不可回收,也就是對象可達。
清除階段:會對未标記的對象進行清理,将記憶體還回,這樣被還回的空間就能夠重新被使用。
回收完畢後記憶體情況就變為如下圖所示:
兩個紅色中間的部分就是本次GC後被釋放的部分。
标記清除算法的缺點就是會導緻記憶體碎片問題。
因為回收的記憶體是不連續的,可能會導緻記憶體碎片問題,這可能會降低記憶體的使用率,比如被回收的記憶體是10KB,那麼說明最大就能容納10KB的對象,如果想放比10KB大的對象,是放不進去的,這也會導緻可能出現頻繁的垃圾回收。
标記整理(Mark Compact)
标記整理也是分為兩個階段,标記和整理。
标記階段:這裡的标記和标記清除裡的标記是一樣的,都是标記出來可達的對象。
整理階段:整理階段其實就是解決标記清除算法帶來的碎片問題,因為在清除的時候,會将可用的對象移動,使其緊湊,這樣就不會出現記憶體碎片。
移動完畢後就變為了下如這樣
此時記憶體中就沒有碎片了,新對象可以在後面繼續配置設定記憶體。
标記整理算法顯然沒有記憶體碎片了,但是他也是有缺點的,由于整理是将對象移動,記憶體位址就會變化,變量引用肯定也要變化。那麼這個效率肯定是低一些的。
複制算法(Copy)
複制算法是将記憶體分為大小相等的兩部分,一部分叫做FROM區,另一部分叫做TO區。複制算法首先也是标記那些存活的可達對象,标記的操作跟之前兩種算法沒有差別。标記後将标記的對象拷貝到另一個區域内,比如對象在FROM區,就拷貝到TO區。
複制完畢後,就可以将FROM區清理掉,并且FROM和TO互換。
在複制算法中TO區域總是空間的空間。
複制算法的有點也是不會産生碎片,它也是有缺點的:記憶體被分為兩半,總是有一半是空閑的,是以記憶體使用率就第了。空閑的記憶體其實挺浪費的。
總結
三種算法在不同的垃圾收集器中都會使用,隻不過根據不同的情況采用不同的垃圾算法而已,不同的垃圾回收器選擇了不同的算法,也應對了不同的情況,擇優選擇。