06.26 JVM內存-GC策略

JVM內存-GC策略

在思考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());
}
}
/<string>/<string>

  • 弱引用也是用來描述非必需對象的,當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());
}
}
/<string>/<string>

  • 虛引用和前面的軟引用、弱引用不同,它並不影響對象的生命週期。在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());
/<string>/<string>/<string>/<string>

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

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

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

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

生存還是死亡

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

回收方法區

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

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

如果對java微服務、分佈式、高併發、高可用、大型互聯網架構技術、面試經驗交流。感興趣可以關注我的頭條號,我會在微頭條不定期的發放免費的資料鏈接,這些資料都是從各個技術網站蒐集、整理出來的,如果你有好的學習資料可以私聊發我,我會註明出處之後分享給大家。歡迎分享,歡迎評論,歡迎轉發。


分享到:


相關文章: