1. 程式人生 > >Android 非靜態內部類/匿名類引起的記憶體洩漏

Android 非靜態內部類/匿名類引起的記憶體洩漏

一、概述

讓我們先來回顧一下android記憶體洩漏的相關概念:
- 記憶體溢位:android系統會給每個安卓程式分配一定的記憶體,當程式所使用的記憶體超過最大值就會造成記憶體溢位,就是常說的OOM
- 記憶體洩漏:簡單來說就是你new了一個物件,這個物件是要消耗記憶體的,然後jvm會對沒有引用的物件進行回收釋放記憶體,如果一個物件已經沒有引用了,但是jvm沒有回收這個物件,就會造成記憶體洩漏,多次記憶體洩漏到最後就會變成記憶體溢位。

二、記憶體洩漏

常見的記憶體洩漏有很多種:
1. 非靜態內部類/匿名內部類的靜態例項容易造成記憶體洩漏
2. 單例模式導致的記憶體洩漏
3. 對該解註冊、登出、清空的物件沒有及時做這樣操作導致的,比如說廣播、服務、io流等等。(其實我個人覺得這一條的最終原因還是第一條,因為說到底還是引用沒有釋放使jvm沒有不能回收)

三、非靜態內部類/匿名內部類的靜態例項容易造成記憶體洩漏

綜上所述,我們來重點理解一下非靜態內部類/匿名內部類的靜態例項容易造成記憶體洩漏,因為以前作者在瞭解android記憶體洩漏方面的知識的時候,對於網上帖子整理出來的常見記憶體洩漏的例子,我會更多的嘗試把它們記住。。因為根本不理解是為什麼會造成記憶體洩漏,所以效率非常低,而且真正碰到了的時候也發現不了。

  • java內部類分為四種
    1. 靜態內部類
    2. 靜態匿名內部類
    3. 非靜態內部類
    4. 非靜態內部匿名類
/**
 * 人類
 */
public class Human {
    private static int age = 1;// 年齡
    private
String name;// 姓名 // 內部類 public class Man{} // 靜態內部類 public static class WoMan{} public void setMan(Man man){} public void setWoMan(WoMan woMan){} public void test(){ Human human = new Human(); // 向human物件setMan方法中傳入一個匿名的Man物件 human.setMan(new Man()); // 這種方式和上面的方式其實是一樣的,大家應該可以看的出來
// 上面的方式就是我們常用的控制元件事件監聽 Man man = new Man(); human.setMan(man); // 向human物件setWoMan方法中傳入一個匿名的Man物件 human.setWoMan(new WoMan()); } }

你們可能會問,靜態內部類和非靜態內部類到底有什麼關係呢?靜態變數大家都會用吧:

private static int age = 1;// 年齡
  • static的東西就代表是直接丟記憶體的,就是我們常說的快取,順便說一句,程式Exception的時候是會清空記憶體的。
    拿上面的例子來說,Human類有一個靜態的變數age,也就是說age是所有Human物件所共享的,換句話說是整個人類都有相同的年齡。而類成員變數name就只屬於單個Human物件,每個人有屬於他自己的名字,並不是共享的。通過這個例子好好理解一下static這個修飾符的概念,後面還會提到。
  • 在Java中,非靜態內部類/匿名類會隱式的持有外部類的引用,像這段程式碼:
human.setMan(new Man());
Man man = new Man();
human.setMan(man);

這樣寫是沒有問題的,內部類隱式持有外部引用,生命週期是相同的,不會造成記憶體洩漏,但是我們來修改一下:

// 在Human裡面定義一個static的Man變數
private static Man whiteMan;// 白人

然後:

whiteMan = new Man();

在test()方法中初始化這個whiteMan,此時,whiteMan是持有持有外部類Human的隱式引用,但是whiteMan是static的,static修飾的變數是放在記憶體中,生命週期是超過Human的,此時就已經發生了記憶體洩漏,類似的android中常見的記憶體洩漏:

public class MyActivity extends AppCompatActivity {
    private static Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        context = this;
    }
}

這是最基本常見的記憶體洩漏,通過我們那面的Human類的例子來理解一下這個記憶體洩漏,首先Activity就相當於Human類,然後static修飾的Context就相當於whiteMan,這個Context是持有Activity的引用的,它的生命週期是超過Activity的,並且這個本該被回收的activty由於它還一直存在著,這就導致了記憶體洩漏。

  • 解決辦法:把上述例子中的Man加上static修飾,如同WoMan。
    用靜態內部類/匿名類替換非靜態內部類/匿名類,因為靜態內部類/匿名類不會隱式的持有外部類引用,外部類會以正常的方式回收。

四、總結

  1. 關鍵詞:static,原理:存放在記憶體中。如果像前面的Human類中那樣的話,一個靜態的變數是屬於所有Human類的例項,而不是屬於單個例項。
  2. 使用靜態內部類/匿名類替換非靜態內部類/匿名類。