1. 程式人生 > >垃圾回收算法

垃圾回收算法

roots 剛才 final 上下文 信息 mil 至少 java語言 屬性

垃圾回收的意義: 

  Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程序員最頭疼的內存管理的問題迎刃而解,它使得Java程序員在編寫程序的時候不再需要考慮內存管理。由於有個垃圾回收機制,Java中的對象不再有“作用域”的概念,只有對象的引用才有“作用域”。垃圾回收可以有效的防止內存泄漏,有效的使用空閑的內存。

任何一種垃圾回收算法一般要做2件基本的事情:(1)發現無用信息對象;(2)回收被無用對象占用的內存空間,使該空間可被程序再次使用。 

對象存活判定算法:

  引用計數算法:每當一個地方引用一個對象,計數器就加1,引用失效的時候,計數器就減1。當計數器為0的時候就表示對象不會再被使用。

缺點:難以解決對象之間相互循環引用的問題。

  可達性分析算法(主流):用過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈,

當一個對象到GC Roots沒有任何引用鏈相連時,則此對象是不可用的。

在 Java 語言裏,可作為 GC Roots 的對象包括下面幾種:

  虛擬機棧(棧幀中的本地變量表)中引用的對象。

  方法區中的類靜態屬性引用的對象。

  方法區中的常量引用的對象。

  本地方法棧中 JNI(Native 方法)的引用對象。

  不過那些發現不能到達 GC Roots 的對象並不會立即回收,在真正回收之前,對象至少要被標記兩次。當第一次被發現不可達時,該對象會被標記一次,同時調用此對象的 finalize()方法(如果有);在第二次被發現不可達後,對象被回收。利用 finalisze() 方法,對象可以逃離一次被回收的命運,但是只有一次。逃命方法如下,只要在 finalize()方法中讓該對象重引用鏈上的任何一個對象建立關聯即可。而如果對象這時還沒有關聯到任何鏈上的引用,那它就會被回收掉。

垃圾回收算法:

  標記-清除算法:首先標記所有需要回收的對象,標記完後在同一回收。

  缺點:效率不高。標記清除後會產生大量不連續的內存碎片。空間碎片太多會導致以後分配較大對象時,需要提前觸發一次垃圾回收。

  復制算法:用於回收新生代。將一塊內存上還存活的對象復制到另一塊上,再一次清除之前的那塊。

  通常用一塊較大的Eden空間,和兩塊較小的Survivor空間。每次使用 Eden 和其中的一塊 Survivor。

  當回收時,將 Eden 和 Survivor 中還存活的對象一次性拷貝到另外一塊 Survivor 空間上,最後清理掉 Eden 和剛才用過的 Survivor 空間。

  Hotspot 虛擬機默認 Eden 和 Survivor 的大小比例是8:1,也就是每次新生代中可用內存空間為整個新生代容量的90%(80% + 10%),只有10%的內存是會被“浪費”的。

  缺點:存在空間浪費。

  標記-整理算法:用於回收老年代。標記後,讓存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。

  缺點:GC暫停時間增長。

  分代收集算法:將java堆分為新生代和老年代,新生代只有少量存活,采用復制算法。而老年代有大批存活,所以采用標記-整理算法或者標記-清除算法。

  增量回收算法如果一次性將所有的垃圾進行處理,需要造成系統長時間的停頓,那麽就可以讓垃圾收集線程和應用程序線程交替執行。

  每次,垃圾收集線程只收集一小片區域的內存空間,接著切換到應用程序線程。依次反復,直到垃圾收集完成。

  缺點:使用這種方式,由於在垃圾回收過程中,間斷性地還執行了應用程序代碼,所以能減少系統的停頓時間。

  但是,因為線程切換和上下文轉換的消耗,會使得垃圾回收的總體成本上升,造成系統吞吐量的下降。

垃圾回收算法