技術問答集錦(17)JVM垃圾回收

1 判斷對象是否可回收有幾種方式?

  1. 引用計數算法

優勢:實現簡單,斷定高效;java

缺點:很難解決對象之間相互循環引用的問題;算法

  1. 可達性分析算法

經過一系列"GC Roots"對象做爲起始點,開始向下搜索,當一個對象到GC Roots沒有任何引用鏈相連時(從GC Roots到這個對象不可達),則證實該對象是不可用的;緩存

優勢:更加精確和嚴謹,能夠分析出循環數據結構相互引用的狀況安全

缺點:實現比較複雜;須要分析大量數據,消耗大量時間;分析過程須要GC停頓(引用關係不能發生變化),即停頓全部Java執行線程(稱爲"Stop The World",是垃圾回收重點關注的問題)數據結構

2 "GC Roots"對象都包含哪些

  1. 虛擬機棧 (棧幀中本地變量表)中引用的對象
  2. 方法區中 類靜態屬性引用的對象
  3. 方法區中 常量引用的對象
  4. 本地方法棧 JNI(Native方法)中引用的對象

3 Java四種引用類型分別是什麼?及存活時間

  1. 強引用:程序代碼廣泛存在的,相似"Object obj=new Object()";只要強引用還存在,GC永遠不會回收被引用的對象;
  2. 軟引用:描述還有用但並不是必需的對象;直到內存空間不夠時(拋出OutOfMemoryError以前),纔會被垃圾回收;最經常使用於實現對內存敏感的緩存;SoftReference類實現;
  3. 弱引用:用來描述非必需對象;只能生存到下一次垃圾回收以前,不管內存是否足夠;WeakReference類實現;
  4. 虛引用:徹底不會對其生存時間構成影響;惟一目的就是能在這個對象被回收時收到一個系統通知;PhantomRenference類實現;

4 Java四種引用使用場景

  1. 強引用-FinalReference

地球人都知道,可是我講不出來;多線程

  1. 軟引用-SoftReference

建立緩存的時候,建立的對象放進緩存中,當內存不足時,JVM就會回收早先建立的對象。PS:圖片編輯器,視頻編輯器之類的軟件可使用這種思路。併發

軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。編輯器

  1. 弱引用-WeakReference

Java源碼中的java.util.WeakHashMap中的key就是使用弱引用,一旦不須要某個引用,JVM會自動處理它,這樣就不須要作其它操做。優化

弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。線程

  1. 虛引用-PhantomReference

主要用來跟蹤對象被垃圾回收器回收的活動。虛引用的回收機制跟弱引用差很少,可是它被回收以前,會被放入ReferenceQueue中。注意哦,其它引用是被JVM回收後才被傳入ReferenceQueue中的。因爲這個機制,因此虛引用大多被用於引用銷燬前的處理工做。

程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。

對象銷燬前的一些操做,好比說資源釋放等。Object.finalize()雖然也能夠作這類動做,可是這個方式即不安全又低效。

5 JVM如何進行對象標記

  1. 第一次標記:在可達性分析後發現到GC Roots沒有任何引用鏈相連時,被第一次標記;而且進行一次篩選:此對象是否必要執行finalize()方法;沒有必要執行的狀況,則標記對象已死;有必要執行的狀況,則對象被放入F-Queue隊列中;
  2. 第二次標記:GC將對F-Queue隊列中的對象進行第二次小規模標記;finalize()方法是對象逃脫死亡的最後一次機會;一個對象的finalize()方法只會被系統自動調用一次,通過finalize()方法逃脫死亡的對象,第二次不會再調用;

6 爲什麼不建議使用finalize()方法

由於其執行的時間不肯定,甚至是否被執行也不肯定(Java程序的不正常退出),並且運行代價高昂,沒法保證各個對象的調用順序(甚至有不一樣線程中調用);若是須要"釋放資源",能夠定義顯式的終止方法,並在"try-catch-finally"的finally{}塊中保證及時調用;

若是有關鍵資源,必須顯式的終止方法;通常狀況下,應儘可能避免使用它,甚至能夠忘掉它;

7 什麼是安全點,爲何須要

運行中,很是多的指令都會致使引用關係變化;若是爲這些指令都生成對應的OopMap,須要的空間成本過高;

只在特定的位置記錄OopMap引用關係,這些位置稱爲安全點(Safepoint);

8 如何選定安全點

不能太少,不然GC等待時間太長;也不能太多,不然GC過於頻繁,增大運行時負荷;

因此,基本上是以程序"是否具備讓程序長時間執行的特徵"爲標準選定,如:方法調用、循環跳轉、循環的末尾、異常跳轉等;

只有具備這些功能的指令纔會產生Safepoint;

9 如何使Java線程在安全點上停頓

  1. 搶先式中斷(Preemptive Suspension):在GC發生時,首先中斷全部線程;若是發現不在Safepoint上的線程,就恢復讓其運行到Safepoint上;
  2. 主動式中斷(Voluntary Suspension):在GC發生時,不直接操做線程中斷,而是僅簡單設置一個標誌;讓各線程執行時主動去輪詢這個標誌,發現中斷標誌爲真時就本身中斷掛起;
  3. 而輪詢標誌的地方和Safepoint是重合的;

10 什麼是安全區域,爲何須要安全區域

線程不執行時沒有CPU時間(Sleep或Blocked狀態),沒法運行到Safepoint上再中斷掛起;

安全區域:指一段代碼片斷中,引用關係不會發生變化;在這個區域中的任意地方開始GC都是安全的;

11 如何使用安全區域解決問題

  1. 線程執行進入Safe Region,首先標識本身已經進入Safe Region;
  2. 線程被喚醒離開Safe Region時,其須要檢查系統是否已經完成根節點枚舉(或整個GC);
  3. 若是已經完成,就繼續執行;不然必須等待,直到收到能夠安全離開Safe Region的信號通知,這樣就不會影響標記結果;

12 GC算法:標記-清楚優缺點

優勢:基於最基礎的可達性分析算法,它是最基礎的收集算法;然後續的收集算法都是基於這種思路並對其不足進行改進獲得的;

缺點:效率問題,標記和清除兩個過程的效率都不高;空間問題,標記清除後會產生大量不連續的內存碎片;這會致使分配大內存對象時,沒法找到足夠的連續內存;從而須要提早觸發另外一次垃圾收集動做;

13 GC算法:複製算法優缺點

優勢:使得每次都是隻對整個半區進行內存回收;內存分配時也不用考慮內存碎片等問題;實現簡單,運行高效;

缺點:空間浪費;效率隨對象存活率升高而變低;

14 GC算法:HotSpot虛擬機複製算法

  1. 將新生代內存分爲一塊較大的Eden空間和兩塊較小的Survivor空間;
  2. 每次使用Eden和其中一塊Survivor;
  3. 當回收時,將Eden和使用中的Survivor中還存活的對象一次性複製到另一塊Survivor;
  4. 然後清理掉Eden和使用過的Survivor空間;
  5. 後面就使用Eden和複製到的那一塊Survivor空間,重複步驟3;

默認Eden:Survivor=8:1,即每次可使用90%的空間,只有一塊Survivor的空間被浪費;

15 什麼是分配擔保

若是另外一塊Survivor空間沒有足夠空間存放上一次新生代收集下來的存活對象時,這些對象將直接經過分配擔保機制(Handle Promotion)進入老年代;

16 GC算法:標記-整理優缺點

優勢:不會產生內存碎片;

缺點:增長了對存活對象須要整理的過程,效率更低;

17 分代收集算法

"分代收集"(Generational Collection)算法結合不一樣的收集算法處理不一樣區域。

新生代:每次垃圾收集都有大批對象死去,只有少許存活;因此可採用複製算法;

老年代:對象存活率高,沒有額外的空間能夠分配擔保;使用"標記-清理"或"標記-整理"算法;

優勢:根據各個年代的特色採用最適當的收集算法;

缺點:仍然不能控制每次垃圾收集的時間;

18 G1垃圾收集算法

19 JVM有哪些收集器?分別用於哪些代?

JDK7/8後,HotSpot虛擬機全部收集器及組合(連線),以下圖:

JVM收集器
新生代收集器:Serial、ParNew、Parallel Scavenge; 老年代收集器:Serial Old、Parallel Old、CMS; 整堆收集器:G1;

20 Serial收集器

新生代、複製算法、單線程收集;

缺點:進行垃圾收集時,必須暫停全部工做線程,直到完成;即會"Stop The World";

Serial/Serial Old組合收集器運行示意圖以下

Serial/Serial Old組合

21 ParNew收集器

新生代、複製算法、多線程收集;

缺點:進行垃圾收集時,必須暫停全部工做線程,直到完成;即會"Stop The World";

ParNew/Serial Old組合收集器運行示意圖以下

ParNew/Serial Old組合

22 Parallel Scavenge收集器

新生代、複製算法、多線程收集;

CMS等收集器的關注點是儘量地縮短垃圾收集時用戶線程的停頓時間;而Parallel Scavenge收集器的目標則是達一個可控制的吞吐量(Throughput);

23 Serial Old收集器

老年代、"標記-整理"算法(還有壓縮,Mark-Sweep-Compact)、單線程收集;

24 Parallel Old收集器

老年代、"標記-整理"算法(還有壓縮,Mark-Sweep-Compact)、多線程收集;

25 CMS收集器

老年代、"標記-清除"算法(不進行壓縮操做,產生內存碎片)、併發收集、低停頓

CMS收集器運行示意圖以下

CMS收集器

整個過程當中耗時最長的併發標記和併發清除均可以與用戶線程一塊兒工做;因此整體上說,CMS收集器的內存回收過程與用戶線程一塊兒併發執行;

CMS收集器3個明顯的缺點

  1. 對CPU資源很是敏感;
  2. 沒法處理浮動垃圾,可能出現"Concurrent Mode Failure"失敗;
  3. 產生大量內存碎片;

26 G1收集器

27 JVM如何進行對象內存分配

在堆上分配(JIT編譯優化後可能在棧上分配),主要在新生代的Eden區中分配;

若是啓用了本地線程分配緩衝,將線程優先在TLAB上分配;

少數狀況下,可能直接分配在老年代中;

分配的細節取決於當前使用哪一種垃圾收集器組合,以及JVM中內存相關參數設置;

28 哪些狀況下對象內存分配會直接進入老年代

  1. 當Eden區沒有足夠空間進行分配時,JVM將發起一次Minor GC(新生代GC);Minor GC時,若是發現存活的對象沒法所有放入Survivor空間,只好經過分配擔保機制提早轉移到老年代。
  2. 須要大量連續內存空間的Java大對象會直接進入老年代,容易提早觸發老年代GC;
  3. 通過屢次Minor GC,若是年齡達到必定程度,就晉升到老年代;
  4. 動態對象年齡斷定:若是在Survivor空間中相同年齡的全部對象大小總和大於Survivor空間的一半,大於或等於該年齡的對象就能夠直接進入老年代;

29 方法區中可回收哪些對象

  1. 廢棄常量:與回收Java堆中對象很是相似;
  2. 無用的類:(1)該類全部實例都已經被回收(即Java椎中不存在該類的任何實例);(2)加載該類的ClassLoader已經被回收,也即經過引導程序加載器加載的類不能被回收;(3)該類對應的java.lang.Class對象沒有在任何地方被引用,沒法在任何地方經過反射訪問該類的方法;

30 JDK HotSpot虛擬機方法區調整

  1. 在JDK7中,使用永久代(Permanent Generation)實現方法區,這樣就能夠不用專門實現方法區的內存管理,但這容易引發內存溢出問題;
  2. 在JDK8中,永久代已被刪除,類元數據(Class Metadata)存儲空間直接在本地內存中分配;
相關文章
相關標籤/搜索