GC JVM垃圾回收

GC(Garbage Collector)就是JVM中自動內存管理機制的具體實現。在HotSpot中,GC的工作任務可以劃分爲兩塊,分別是內存的動態分配和垃圾回收。在進行內存動態分配之前,GC首先會對內存空間進行劃分。目前幾乎所有的GC都是採用分大收集算法進行垃圾回收的,所以Java堆區進一步細分的話,還可以劃分爲新生代(YoungGen)和老年代(OldGen),其中新生代還可以劃分爲Eden空間、From Survivor空間和To Survivior空間,換句話說,內存空間究竟如何分配完全依賴於GC的設計。當內存空間劃分完成後,GC就可以爲新對象分配內存空間,並區分出存儲在內存中的對象哪些是存活的,那些是死亡的,如果對象已經死亡的,就可以將其標記爲垃圾。GC在進行內存回收時應該做到高效,不應該導致應用程序出現長時間的暫停,以及避免產生內存碎片。
垃圾標記:根搜索算法
當一個對象已經不再被任何的存活對象繼續引用時,就可以宣判爲已經死亡。目前有兩種常見的垃圾標記算法,分別是計數算法和根搜索算法。

  • 計數算法:技術算法回味程序中的每一個對象都創建一個私有的引用計數器,當目標兌現被其他存活的對象引用時,引用計數器則會+1,不再引用時則會-1,當引用計數器的值爲0時,意味着該對象已經不再被任何對象引用,可以被標記爲垃圾對象。但是如果一些明顯已經死亡的對象儘管沒有被任何存活的對象引用,但是他們之間互相引用,計數器的值永遠不會爲0,永遠不會被清理,造成內存泄漏。
  • 根搜索算法:根搜索算法是以根對象集合爲起始點,按照從上到下的方式搜索被根對象集合所連接的目標表對象是否可到達,如果目標對象不可到達,則意味着該對象已經死亡,便可以在InstancceOopDesc的Mark World中將其標記爲垃圾對象。

根對象集合包含了以下的五種元素:

  • Java棧中的對象引用
  • 本地方法棧中的對象引用
  • 運行時常量池中的對象引用
  • 方法區中類靜態屬性的對象引用
  • 與一個類對應的唯一數據類型的Class對象

垃圾回收:分代收集算法

主要分爲三種:

  • 標記清除算法(Mark-Sweep)
  • 複製算法(Copying)
  • 標記-壓縮算法(Mark-Compact)

標記清除算法:相對與另外兩種算法而言,不僅執行效率低下,更重要的是由於被執行回收的無用對象所佔用的內存空間有可能是不連續的內存塊,不可避免的會產生內存碎片,從而導致後續沒有足夠的可用內存空間非配給較大的對象。
複製算法:在HotSpot中,Eden空間和另外兩種Survivor空間默認所佔的比例爲8:1:1。當執行一次Minor GC時,Eden空間中存活的對象就會被複制到To空間內,並且之前已經經歷過一次Minor GC並在From空間存活下來的對象如果還年輕的話同樣也會被複制到To空間內。在以下兩種情況下,Eden空間和From空間中的存活對象將不會被複制到To空間內:首先是如果存活的對象的分代年齡超過選項“-XX:MaxTenuringThreshold”所指定的閥值時,將會直接晉升到老年代中,其次是當To空間的容量達到閥值時,存活對象同樣也是直接晉升到老年代中。當所有的存活對象被複制到To空間或者晉升到老年代後,剩餘的均爲垃圾對象,執行Minor GC。當執行玩Minor GC之後,Eden空間和From空間將會被清空,而存活下來的對象則會被全部存儲在To空間之內,接下來From空間和To空間將會互換位置。
標記壓縮算法:儘管複製算法能夠高效的執行Minor GC,但是他卻不適合老年代的內存回收。因爲老年代中對象的生命週期較長,甚至在某些極端的情況下還能夠與JVM的生命週期保持一致,所以如果老年代中的對象也採用複製算法回收內存,不僅需要額外的時間和空間,而且還會導致較多的複製操作影響到GC的執行效率。標記壓縮算法:當成功的標記出內存中的垃圾對象後,該算法會將所有的存活對象都移動到一個規整且連續的內存空間中,然後執行Full GC(老年代的垃圾回收)回收無用對象所佔用的內存空間。當成功執行壓縮以後,已用和未用的內存都各自一邊,彼此之間維繫着一個記錄下一次分配起始點的標記指針,當爲新對象分配內存時,則可以使用指針碰撞技術修改指針的偏移量將新對象分配在第一個空閒內存位置上,爲新對象分配內存帶來便捷。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章