Java對象循環引用,Java gc 如何回收

在C++中使用過智能指針的同學們應該都清楚智能指針對C++中內存管理帶來的極大便利,但是也會引入一些頭疼的問題,比如智能指針帶來的循環引用的問題,這個問題在之前的項目中一直沒有很好的解決。
        最近參與到android的項目開發,對java的內存的管理有了一個初步的瞭解,很容易想到了循環引用的問題。比如下面這個例子:
        public void buidDog()
        {
           Dog newDog = new Dog();
          Tail newTail = new Tail();
          newDog.tail = newTail;
          newTail.dog = newDog;
        }
        在這裏,newTail中拿着對newDog的引用,newDog中拿着對newTail的引用。如果newDog要被回收,前提是newTail被先回收,這樣才能釋放對newDog的引用。但是反回過來,newTail要被回收的前提是newDog要被先回收。當buildDog函數退出後,看起來垃圾回收管理似乎就始終無法回收這兩個實際已經不再需要的對象。
         垃圾回收機制究竟能否解決循環引用這一困境,帶着這個疑問找了一些資料,找到了一個比較滿意的解釋。在《Java Platform Performance: Strategies and Tactics》這本書的附錄A中有一處說明,這本書出自sun公司java團隊員工,應該算比較權威的。其中有這樣一段(http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html#997428):
   “It's important to note that not just any strong reference will hold an object in memory. 
    These must be references that chain from a garbage collection root. 
    GC roots are a special class of variable that includes 
Temporary variables on the stack (of any thread)
        Static variables (from any class)
        Special references from JNI native code”。
這段話可以簡單的理解就是強引用並不能保證對象不被回收。垃圾回收機制除了檢查對象是否被引用外,還要看對象是否被至少一個GC roots對象直接或者間接引用。GC roots對象包括以下一些類容:
1 每個線程當前的函數調用棧,從棧頂到棧底的每個函數裏的局部變量。
2 靜態的變量
3 被jni中引用到的變量。
        所以,上面例子中兩個循環引用的對象,雖然都存在一個強引用,但是不被任何GC root對象直接或者間接引用到,垃圾回收機制能夠發現這個問題。
        另外,爲了驗證這一點,特意翻看了一下android源碼中GC管理這一塊的代碼。在MarkSweep.c這文件中,有一個void dvmHeapMarkRootSet()函數,這個函數對於GC root對象,有一些詳細的說明,有興趣的可以細看一下。
        所以,java對於循環引用有一套自己的解決方案。但是話又說回來,一般實際編碼中出現的循環引用不會是上面那個例子那樣明顯,一般都是多個對象複雜的引用導致的循環,這個時候,如果一個對象的生命週期很長,就會導致多個對象都釋放不了,所以還是要特別留意對象之間的引用關係。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章