14

我一直在研究在創建視圖時防止上下文/活動內存泄漏的最佳實踐,並且我無法在它發生什麼或不被允許時找到明確的答案來到類中的靜態字段。Android:靜態字段和內存泄漏

比方說,我有這種形式的代碼:

public class MyOuterClass extends Activity{ 
    private MyInnerClass; 
    MyInnerClass = (MyInnerClass) findViewById(<XML call here>); 
    MyInnerClass.myXInt = 3; 

    // onCreate(), onResume(), etc. 

    public static class MyInnerClass extends SurfaceView implements Runnable{ 
     // Safe variables? 
     private static int myXInt, myYInt; 
     private static boolean myBoolean; 
     // Potentially safe? 
     private static Canvas myCanvas; 
     // Definitely bad. 
     private static Context myContext; 

     public MyInnerClass(Context context){ 
     myContext = context;  // This is bad. 
     } 
    } 
} 

我就什麼JVM實際上考慮了MyInnerClass ClassLoader的輕度的意識模糊。從技術上講,因爲它是一個SurfaceView對象,所以一旦應用程序一次實例化MyInnerClass(發生在View第一次膨脹時),靜態變量應該總是存在,然後一直存在,直到應用程序本身終止。如果是這種情況,那麼防止Bitmaps和Canvas對象保持打開狀態並填充堆?

我一遍又一遍地看到的唯一的聲明是你不能像構造函數中顯示的那樣泄露靜態上下文,但它永遠不會超越這個。那真的是你唯一不能做的事情嗎?

+0

你的Canvas等不需要是靜態的。這樣它將永遠留在堆中 – zapl 2012-08-10 19:44:56

+1

如果是這種情況,那麼什麼會阻止常量(即 - private static final int MY_CONSTANT)從持有任何擴展活動(及其上下文)的類打開? – SeaNick 2012-08-10 19:48:37

回答

27

在Java/Android a static變量或常數不會被垃圾收集。只要保存它的類通過類加載器加載,它就會停留在那裏。類加載器對於應用程序中的所有類都是一樣的,它對所有類都有靜態引用(例如MyInnerClass.class)。由於類加載器不會消失,所以您的類不會這樣做,因爲它們被引用爲&因此不是垃圾收集。

就像在你的榜樣

public class SomeClass extends SurfaceView { 
    private static Context myContext; 

    public MyInnerClass(Context context){ 
    myContext = context;  // This is bad. 
    } 
} 

這確實是壞的。即使不存在對SomeClass的引用(例如Activity顯示您的自定義SurfaceView已結束),仍會保留對Context(和SomeClass中的任何其他static變量/常量的靜態引用。您可以認爲它們都是泄漏的,因爲它是不可能的垃圾收集Context等如果你有一個常規的變量引用的東西,那麼一旦包含該變量的實例沒有更多的引用它的整個實例,包括其對其他東西的引用可以並且將被垃圾回收Java甚至可以處理循環引用很好

對於你想要發生的常量,它通常並不壞,因爲它們佔用的常量和內存量並不大,而且常量不會(不應該)引用其他佔用大量內存的實例,如ContextBitmap

除了通過靜態變量創建內存泄漏的可能性之外,如果您不想在同一時間只對所有實例只有一件事情,也可能會產生問題。例如,如果將SurfaceViewBitmap保存在static變量中,則不能有兩個不同的圖像。即使兩個SurfaceView不同時顯示,您可能會遇到問題,因爲每個新的實例可能會覆蓋舊的圖像,如果您回到另一個SurfaceView您意外地顯示錯誤的圖像。我幾乎可以肯定你不想在這裏使用static

事實上,你的內部類是static class並不意味着你必須使用靜態變量 - 它只是意味着它更像一個static方法,因爲它不能使用實例變量(那些不是static)。

爲了避免內存泄漏,您根本不應該使用靜態變量。沒有必要使用它們,除非你做了特殊的事情(例如計算一個類的實例)。常量很好。

+0

謝謝。我猜想想要靜態的總體原因是因爲在概念上,大多數View對象只需要一個實例,因爲它們通常表示屏幕上的特定屏幕或對象。使它們變爲靜態代碼更有效,但我明白其中的缺點。唯一令我困擾的是,如何將變量完全留在GC中是因爲我無法控制什麼時候需要釋放內存,並且從我在應用程序中看到的所有內容都始終處於位圖操作期間,恐怕會變成抖動幀率。 – SeaNick 2012-08-13 16:50:08

+1

你不需要有一些靜態的東西來保持它的活力和重用。只要你有一個參考它就會存在。如果您忘記將所有靜態引用正確地「空」,那麼使用靜態通常會導致更多的內存消耗。 +如果您旋轉屏幕,例如 – zapl 2012-08-13 17:06:48

+1

,通常會重新創建視圖實例我保持屏幕方向鎖定,但這是一個很好的觀點。再次感謝! – SeaNick 2012-08-13 17:47:52