1. 程式人生 > >深入理解java虛擬機---垃圾收集器和分配策略-1

深入理解java虛擬機---垃圾收集器和分配策略-1

public 空間 說明 java ref 方法 虛引用 頻繁 print

博文重點:

    學習目標:哪些內存需要回收

         什麽時候回收

         如何回收

    在基於概念討論的模型中,主要對Java堆和方法區進行討論。

    why?:一個接口中的多個實現類需要的內存可能不一樣,一個方法中的多個分支需要的內存也可能不一樣。只有在程序運行期間才能知道會創建哪些對象,這部分內存的分配和回收都是動態的,gc關註的就是這一塊內存。

哪些內存需要回收:

        判斷對象是否存活:

                引用計數算法:對象中添加一個引用計數器,有一個地方引用它則計數器加1,引用失效時,減1。引用為0的對象就是不可使用的。

                  優點:實現簡單,判定效率高。

                  缺點:無法解決對象之間的循環引用,見代碼。

技術分享圖片
 1 public class ReferenceCountingGC {
 2     public Object instance = null;
 3 
 4     private static final int _1MB = 1024 * 1024;
 5 
 6     private byte[] bigSize = new byte[2 * _1MB];
 7 
 8     public static void testGC() {
9 ReferenceCountingGC objA = new ReferenceCountingGC(); 10 ReferenceCountingGC objB = new ReferenceCountingGC(); 11 objA.instance = objB; 12 objB.instance = objA; 13 14 15 objA = null; 16 objB = null; 17 18 // 雖然引用計數都為1,但內存還是被回收了,說明采用的不是引用計數算法
19 System.gc(); 20 } 21 22 public static void main(String[] args) { 23 testGC(); 24 } 25 }
View Code

                可達性分析算法:思路,選擇一系列稱為"GC Roots"的對象作為起始點,從這些節點向下搜索,走過的路就稱為引用鏈。如果一個對象無法通過引用鏈到達"GC roots",則證明該對象不可用,則可被回收。

                  可作為GC Roots的對象:虛擬機棧中引用的對象,方法區類靜態屬性引用的對象,方法區中常量引用的對象,Nativa方法中引用的對象。 todo:理解gc roots

技術分享圖片

    引用:

      todo:各種應用場景

      引用細化定義:當內存空間還足夠,則能保留在內存中。如果內存空間進行垃圾收集之後還是非常緊張,則拋棄這些對象。

      基於這樣的需求,擴充了引用的概念。

      強引用:只要強引用存在,就永遠不會被gc。eg. Object obj = new Object();

      軟引用:內存充足時不會回收,不足時被回收。jvm將這個軟引用加入到與之關聯的引用隊列

      弱引用:無論內存是否充足,都會進行回收。jvm將這個弱引用加入到與之關聯的引用隊列

      虛引用:

    

    對象的兩次標記:如果對象在進行第一次可達性分析之後,沒有到gc roots到引用鏈,則進行第一次標記。並進入第一次自救過程,如果該對象重寫了finalize()方法時 && finalize()方法沒有被虛擬機調用過,則會執行finalize()方法進行自救過程,將該對象放入到一個F-Queue到隊列中,由虛擬機自動建立的,低優先級的Finalize線程去執行(但是不保證會等待方法運行結束,為了效率考慮)。如果在finalize()方法中將該對象的引用賦值給了類變量或成員變量,重新建立起了可達關系,則在該第二次標記過程會被移出"即將回收"集合,自救成功,但要註意,這樣的自救只能執行一次。

    

技術分享圖片
 1 public class FinalizeEscapeGC {
 2     public static FinalizeEscapeGC SAVE_HOOK = null;
 3     public void isAlive() {
 4         System.out.println("yes , i am still alive");
 5     }
 6 
 7     @Override
 8     protected void finalize() throws Throwable {
 9         System.out.println("finalize method excute!");
10         FinalizeEscapeGC.SAVE_HOOK = this;
11     }
12 
13     public static void main(String[] args) throws InterruptedException {
14         SAVE_HOOK = new FinalizeEscapeGC();
15 
16         // 第一次拯救自己成功
17         SAVE_HOOK = null;
18         System.gc();
19 
20         Thread.sleep(500);
21         if(SAVE_HOOK != null) {
22             SAVE_HOOK.isAlive();
23         } else {
24             System.out.println("dead");
25         }
26 
27         // 第二次拯救自己失敗,只能執行一次
28         SAVE_HOOK = null;
29         System.gc();
30 
31         Thread.sleep(500);
32         if(SAVE_HOOK != null) {
33             SAVE_HOOK.isAlive();
34         } else {
35             System.out.println("dead");
36         }
37     }
38 }
View Code

    方法區(永久代)的回收:主要回收廢棄常量和無用類。

                廢棄常量:eg:"abc"存在常量池中,但沒有其它地方引用這個常量,類,方法,字段的符號引用也和這個類似。

                無用類:該類所有實例已被回收

                    加載該類的ClassLoader已被回收

                    對應的Class對象沒有被引用,無法在其它地方通過反射訪問該類的方法。

               滿足了這些條件的類可以被回收,是否進行回收,取決於我們對虛擬機的參數設置情況。

                使用場景:在大量使用反射,動態代理,CGLib等頻繁定義自ClassLoader的場景都需要虛擬機具備類卸載的功能

深入理解java虛擬機---垃圾收集器和分配策略-1