當初遇到這個問題debug超久直到看完了以下google io影片後才有眉目,
http://www.youtube.com/watch?v=_CruQY55HOk
此篇為此影片的subset, 有閒的推薦直接看影片 (以下圖片來源為影片截圖)
故事是從這段外表看似簡單,leak卻過於常人的code開始的
狀況敘述:
Android 的Garbage collector只有在物件沒有被任何別的物件reference到的時候才回將這個物件回收.
當上圖這個Activity離開的時候理當被Garbege collector回收,但是因為leak物件依然reference這個Activity,所以此Activity不會被回收,
若同時這個Activity reference到一些Bitmap的話, 則這些Bitmap也不會被回收掉!!
導致多start幾次這個activity馬上就會噴out of memory.
狀況詳解:
問題的關鍵在於1.Leaky為一個inner class, 且2.leak為static object.
這兩件事情一同造成了MainActivity不會被garbage collect:
1. java non-static inner class 在實例化的時候固定會有一個reference指向其enclose class(包住這個inner class的class)
這個特性是為了能在inner class中使用其enclose class中的資源,例如
public class MainActivity extends Activity
{
class Leaky
{
void doSomething()
{
MainActivity.this.someFunction();
}
}
}
這就是為什麼上圖中leaky物件指向MainActivity.
2. java class中的static variable 會在java class loader load class 後儲存在heap中一段稱為Permanent Genration的特如區域中. 而這區域在process結束之前都不會被回收.
因為leak物件為static的關係所以
leak = new Leaky() 這行程式碼建立了上圖中MainActivity class 到leaky的連結.
結果因為leak不會被回收導致被leak reference到的MainActivity就算離開了也還是存在著一條reference而不會被回收.
同樣的情況不只會發生在inner class, anonymous class一樣會產生一條對enclose class的連結,所以也可能會導致一樣的狀況
一般使用static object是不會出問題的, 但是要特別注意這個static object是否為一個inner class或anonymous class.
很多的memory leak都跟static脫不了關西, static與singleton雖然好用,但使用上要特別小心.
沒有留言:
張貼留言