JVM記憶體-GC策略

YZhangExpress發表於2019-02-14

在思考GC時通常需要完成3件事情:

  • 哪些記憶體需要回收?
  • 什麼時候回收?
  • 如何回收?

引用計數器演算法GC存在的問題是?


描述:引用計數器法給物件中新增一個引用計數器,沒當一個地方引用它,計數器就加一,引用失效時減一,任何引用計數器為0的物件就是不可能再被使用的物件。

引用計數器演算法的效率也比較高,主流的JVM裡面並沒有選用引用計數器來管理記憶體,其中最主要的問題是不能解決物件之間迴圈引用的問題。

jvm可達性分析演算法

java是通過可達性分析來判斷物件是否存活,這個演算法的基本思路是通過一系列的稱為“GC Roots”的物件為起始點,從這些節點向下搜尋,搜尋所有的路徑稱為引用鏈;引用鏈不能到達的物件節點是GC需要回收的。

在java語言中可被稱為“GC Roots”的物件包括下面幾個方面:

  • 1.虛擬機器棧中引用的物件
  • 2.方法區中類靜態屬性引用的物件
  • 3.方法區中常量引用的物件
  • 4.本地方法(Native)引用的物件

無論是引用計數器還是可達性分析判斷的標準都是物件是否引用。

在jdk1.2後,java對引用進行了擴充,將引用分為強引用 、軟引用、弱引用、虛引用4種,強弱依次遞減。

  • 強引用:類似“Object obj = new Object()”,只要存在,GC永不回收
public class Main {
    public static void main(String[] args) {
        new Main().fun1();
    }
     
    public void fun1() {
        Object object = new Object();
        Object[] objArr = new Object[1000];
    }
}
複製程式碼
  • 軟引用:軟引用是用來描述一些有用但並不是必需的物件,在Java中用java.lang.ref.SoftReference類來表示。對於軟引用關聯著的物件,只有在記憶體不足的時候JVM才會回收該物件。如:
import java.lang.ref.SoftReference;
 
public class Main {
    public static void main(String[] args) {
         
        SoftReference<String> sr = new SoftReference<String>(new String("hello"));
        System.out.println(sr.get());
    }
}
複製程式碼

  • 弱引用也是用來描述非必需物件的,當JVM進行垃圾回收時,無論記憶體是否充足,都會回收被弱引用關聯的物件。在java中,用java.lang.ref.WeakReference類來表示。下面是使用示例:
public class Main {
    public static void main(String[] args) {
     
        WeakReference<String> sr = new WeakReference<String>(new String("hello"));
         
        System.out.println(sr.get());
        System.gc();                //通知JVM的gc進行垃圾回收
        System.out.println(sr.get());
    }
}
複製程式碼

  • 虛引用和前面的軟引用、弱引用不同,它並不影響物件的生命週期。在java中用java.lang.ref.PhantomReference類表示。如果一個物件與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收。
ReferenceQueue<String> queue = new ReferenceQueue<String>();
        PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
        System.out.println(pr.get());
複製程式碼

如何利用軟引用和弱引用解決OOM問題

  前面講了關於軟引用和弱引用相關的基礎知識,那麼到底如何利用它們來優化程式效能,從而避免OOM的問題呢?

  下面舉個例子,假如有一個應用需要讀取大量的本地圖片,如果每次讀取圖片都從硬碟讀取,則會嚴重影響效能,但是如果全部載入到記憶體當中,又有可能造成記憶體溢位,此時使用軟引用可以解決這個問題。

  設計思路是:用一個HashMap來儲存圖片的路徑 和 相應圖片物件關聯的軟引用之間的對映關係,在記憶體不足時,JVM會自動回收這些快取圖片物件所佔用的空間,從而有效地避免了OOM的問題。在Android開發中對於大量圖片下載會經常用到。
  
  

生存還是死亡

要宣告一個物件死亡,至少經歷兩次標記過程:如果進行可達性分析時沒有到達的引用鏈,將會做第一次標記,此處需要進行一次篩選,條件是有必要執行finalize()方法,當物件沒有覆蓋finalize(),或者已經被jvm呼叫,這兩種情況稱為“沒有必要執行”;如果物件被判斷有必要執行,會被放置在一個F-Queue佇列,稍後有優先順序低的執行緒進行第二次標記,如果物件finalize重新進行了引用,那麼將會移出回收佇列,否則會被進行垃圾回收。

回收方法區

判斷一個類需要回收的條件:

  • 該類的所有例項已經被回收,也就是堆中不存在該類的例項
  • 載入該類的ClassLoader已經被回收
  • 該類的Class物件沒有在任何地方引用,沒有使用到反射的方法
      
      
      
      

參考書籍: 深入理解jvm

引用部落格: https://www.cnblogs.com/dolphin0520/p/3784171.html

相關文章