深入理解java虛擬機---垃圾收集器和分配策略-1
博文重點:
學習目標:哪些內存需要回收
什麽時候回收
如何回收
在基於概念討論的模型中,主要對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() {View Code9 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 }
可達性分析算法:思路,選擇一系列稱為"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