關於java中對象的循環引用問題

  在C++中使用過智能指針的同窗們應該都清楚智能指針對C++中內存管理帶來的極大便利,可是也會引入一些頭疼的問題,好比智能指針帶來的循環引用的問題,這個問題在以前的項目中一直沒有很好的解決。

        最近參與到android的項目開發,對java的內存的管理有了一個初步的瞭解,很容易想到了循環引用的問題。好比下面這個例子:html

        public void buidDog()java

        {android

           Dog newDog = new Dog();函數

          Tail newTail = new Tail();ui

          newDog.tail = newTail;編碼

         newTail.dog = newDog;線程

        }指針

        在這裏,newTail中拿着對newDog的引用,newDog中拿着對newTail的引用。若是newDog要被回收,前提是newTail被先回收,這樣才能釋放對newDog的引用。可是反回過來,newTail要被回收的前提是newDog要被先回收。當buildDog函數退出後,看起來垃圾回收管理彷佛就始終沒法回收這兩個實際已經再也不須要的對象。code

         垃圾回收機制究竟可否解決循環引用這一困境,帶着這個疑問找了一些資料,找到了一個比較滿意的解釋。在《Java Platform Performance: Strategies and Tactics》這本書的附錄A中有一處說明,這本書出自sun公司java團隊員工,應該算比較權威的。其中有這樣一段(http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html#997428):orm

「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對於循環引用有一套本身的解決方案。可是話又說回來,通常實際編碼中出現的循環引用不會是上面那個例子那樣明顯,通常都是多個對象複雜的引用致使的循環,這個時候,若是一個對象的生命週期很長,就會致使多個對象都釋放不了,因此仍是要特別留意對象之間的引用關係。

相關文章
相關標籤/搜索