Android 非靜態內部類/匿名類引起的記憶體洩漏
一、概述
讓我們先來回顧一下android記憶體洩漏的相關概念:
- 記憶體溢位:android系統會給每個安卓程式分配一定的記憶體,當程式所使用的記憶體超過最大值就會造成記憶體溢位,就是常說的OOM
- 記憶體洩漏:簡單來說就是你new了一個物件,這個物件是要消耗記憶體的,然後jvm會對沒有引用的物件進行回收釋放記憶體,如果一個物件已經沒有引用了,但是jvm沒有回收這個物件,就會造成記憶體洩漏,多次記憶體洩漏到最後就會變成記憶體溢位。
二、記憶體洩漏
常見的記憶體洩漏有很多種:
1. 非靜態內部類/匿名內部類的靜態例項容易造成記憶體洩漏
2. 單例模式導致的記憶體洩漏
3. 對該解註冊、登出、清空的物件沒有及時做這樣操作導致的,比如說廣播、服務、io流等等。(其實我個人覺得這一條的最終原因還是第一條,因為說到底還是引用沒有釋放使jvm沒有不能回收)
三、非靜態內部類/匿名內部類的靜態例項容易造成記憶體洩漏
綜上所述,我們來重點理解一下非靜態內部類/匿名內部類的靜態例項容易造成記憶體洩漏,因為以前作者在瞭解android記憶體洩漏方面的知識的時候,對於網上帖子整理出來的常見記憶體洩漏的例子,我會更多的嘗試把它們記住。。因為根本不理解是為什麼會造成記憶體洩漏,所以效率非常低,而且真正碰到了的時候也發現不了。
- java內部類分為四種
- 靜態內部類
- 靜態匿名內部類
- 非靜態內部類
- 非靜態內部匿名類
/**
* 人類
*/
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。
用靜態內部類/匿名類替換非靜態內部類/匿名類,因為靜態內部類/匿名類不會隱式的持有外部類引用,外部類會以正常的方式回收。
四、總結
- 關鍵詞:static,原理:存放在記憶體中。如果像前面的Human類中那樣的話,一個靜態的變數是屬於所有Human類的例項,而不是屬於單個例項。
- 使用靜態內部類/匿名類替換非靜態內部類/匿名類。