2013年5月15日 星期三

Android 恐怖的Out of memory exception 之:死不了的Activity


當初遇到這個問題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雖然好用,但使用上要特別小心.



沒有留言:

張貼留言