【深入Java虚拟机JVM 07】JVM如何判断对象已死
說明:文章所有內容均摘自《深入理解Java虛擬機:JVM高級特性與最佳實踐(第二版)》
?
?
在堆里面存放著Java世界中幾乎所有的對象實例,垃圾收集器在對堆進行回收前,第一件事情就是要確定這些對象之中哪些還“存活”著,哪些已經“死去”(即不可能再被任何途徑使用的對象)。
?
1 引用計數算法
很多教科書判斷對象是否存活的算法是這樣的:給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的對象就是不可能再被使用的。作者面試過很多的應屆生和一些有多年工作經驗的開發人員,他們對于這個問題給予的都是這個答案。
客觀地說,引用計數算法(Reference Counting)的實現簡單,判定效率也很高,在大部分情況下它都是一個不錯的算法,也有一些比較著名的應用案例,例如微軟公司的COM(Component Object Model)技術、使用ActionScript 3的FlashPlayer、Python語言和在游戲腳本領域被廣泛應用的Squirrel中都使用了引用計數算法進行內存管理。但是,至少主流的Java虛擬機里面沒有選用引用計數算法來管理內存,其中最主要的原因是它很難解決對象之間相互循環引用的問題。
舉個簡單的例子,請看代碼清單3-1中的testGC()方法:對象objA和objB都有字段instance,賦值令objA.instance=objB及objB.instance=objA,除此之外,這兩個對象再無任何引用,實際上這兩個對象已經不可能再被訪問,但是它們因為互相引用著對方,導致它們的引用計數都不為0,于是引用計數算法無法通知GC收集器回收它們。
/***testGC()方法執行后,objA和objB會不會被GC呢?*@author zzm*/ public class ReferenceCountingGC{public Object instance=null;private static final int_1MB=1024*1024;/***這個成員屬性的唯一意義就是占點內存,以便能在GC日志中看清楚是否被回收過*/private byte[]bigSize=new byte[2*_1MB];public static void testGC(){ReferenceCountingGC objA=new ReferenceCountingGC();ReferenceCountingGC objB=new ReferenceCountingGC();objA.instance=objB;objB.instance=objA;objA=null;objB=null; //假設在這行發生GC,objA和objB是否能被回收?System.gc();} }//運行結果 [Full GC(System)[Tenured:0 K->210K(10240K),0.0149142secs]4603K->210K(19456K),[Perm:2999K-> 2999K(21248K)],0.0150007 secs][Times:user=0.01 sys=0.00,real=0.02 secs] Heap def new generation total 9216K,used 82K[0x00000000055e0000,0x0000000005fe0000,0x0000000005fe0000) Eden space 8192K,1%used[0x00000000055e0000,0x00000000055f4850,0x0000000005de0000) from space 1024K,0%used[0x0000000005de0000,0x0000000005de0000,0x0000000005ee0000) to space 1024K,0%used[0x0000000005ee0000,0x0000000005ee0000,0x0000000005fe0000) tenured generation total 10240K,used 210K[0x0000000005fe0000,0x00000000069e0000,0x00000000069e0000) the space 10240K,2%used[0x0000000005fe0000,0x0000000006014a18,0x0000000006014c00,0x00000000069e0000) compacting perm gen total 21248K,used 3016K[0x00000000069e0000,0x0000000007ea0000,0x000000000bde0000) the space 21248K,14%used[0x00000000069e0000,0x0000000006cd2398,0x0000000006cd2400,0x0000000007ea0000) No shared spaces configured.從運行結果中可以清楚看到,GC日志中包含“4603K->210K”,意味著虛擬機并沒有因為這兩個對象互相引用就不回收它們,這也從側面說明虛擬機并不是通過引用計數算法來判斷對象是否存活的。
?
2. 可達性分析算法
在主流的商用程序語言(Java、C#,甚至包括前面提到的古老的Lisp)的主流實現中,都是稱通過可達性分析(Reachability Analysis)來判定對象是否存活的。這個算法的基本思路就是通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference ?Chain),當一個對象到GC ?Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個對象不可達)時,則證明此對象是不可用的。如圖3-1所示,對象object 5、object 6、object 7雖然互相有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的對象。
在Java語言中,可作為GC Roots的對象包括下面幾種:
- 虛擬機棧(棧幀中的本地變量表)中引用的對象。
- 方法區中類靜態屬性引用的對象。
- 方法區中常量引用的對象。
- 本地方法棧中JNI(即一般說的Native方法)引用的對象。
總結
以上是生活随笔為你收集整理的【深入Java虚拟机JVM 07】JVM如何判断对象已死的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【深入Java虚拟机JVM 06】垃圾收
- 下一篇: 【深入Java虚拟机JVM 08】JVM