Android內存優化10 內存泄漏常見情況1 靜態泄漏


1,內存泄漏到本質是該釋放的對象被持久化的對象引用了,造成持久化的常見情況有1,靜態持久化 2,線程持久化

線程持久化

因為存活的線程是有dvk虛擬久直接持有,所以存活的線程都是持久化的

內存泄漏1:靜態Activities(static Activities)

代碼如下:
MainActivity.Java

public class MainActivity extends AppCompatActivity { private static MainActivity activity; TextView saButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); saButton = (TextView) findViewById(R.id.text); saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticActivity(); nextActivity(); } }); } void setStaticActivity() { activity = this; } void nextActivity(){ startActivity(new Intent(this,RegisterActivity.class)); SystemClock.sleep(1000); finish(); } @Override protected void onDestroy() { super.onDestroy(); //使用LeakCanary觀察是否有內存泄漏 MyApplication.getRefWatcher().watch(this); } }

 

LeakCanary檢測出的內存泄漏:

這里寫圖片描述

為什么?
在上面代碼中,我們聲明了一個靜態的Activity變量並且在TextView的OnClick事件里引用了當前正在運行的Activity實例,所以如果在activity的生命周期結束之前沒有清除這個引用,則會引起內存泄漏。因為聲明的activity是靜態的,會常駐內存,如果該對象不清除,則垃圾回收器無法回收變量。

怎么解決?
最簡單的方法是在onDestory方法中將靜態變量activity置空,這樣垃圾回收器就可以將靜態變量回收。

@Override protected void onDestroy() { super.onDestroy(); activity = null; //使用LeakCanary觀察是否有內存泄漏 MyApplication.getRefWatcher().watch(this); }

 不使用靜態activity,或給靜態activity賦值時,考慮賦值的activity生命周期是不是全局的,或者在靜態activity使用完后及時釋放

內存泄漏2:靜態View

代碼如下:
MainActivity.java

    ...
    private static View view; TextView saButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); saButton = (TextView) findViewById(R.id.text); saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticView(); nextActivity(); } }); } void setStaticView() { view = findViewById(R.id.sv_view); } ...

 

LeakCanary檢測到的內存泄漏

這里寫圖片描述

為什么?
上面代碼看似沒有問題,在Activity里聲明一個靜態變量view,然后初始化,當Activity生命周期結束了內存也釋放了,但是LeakCanary卻顯示出現了內存泄漏,為什么?問題出在這里,View一旦被加載到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是我們的Activity,聲明一個靜態變量引用這個View,也就引用了activity,所以當activity生命周期結束了,靜態View沒有清除掉,還持有activity的引用,因此內存泄漏了。

怎么解決?
在onDestroy方法里將靜態變量置空。

@Override protected void onDestroy() { super.onDestroy(); view = null; MyApplication.getRefWatcher().watch(this); } 
不使用靜態view,或在activity關閉時將靜態view賦值為null

內存泄漏3:靜態內部類

代碼如下:
MainActivity.java

private static Object inner; void createInnerClass() { class InnerClass { } inner = new InnerClass(); } View icButton = findViewById(R.id.ic_button); icButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { createInnerClass(); nextActivity(); } }); 
 

使用LeakCanary檢測到的內存泄漏:

這里寫圖片描述

為什么?
非靜態內部類會持有外部類的引用,在上面代碼中內部類持有Activity的引用,因此inner會一直持有Activity,如果Activity生命周期結束沒有清除這個引用,這樣就發生了內存泄漏。

怎么解決?
因為非靜態內部類隱式持有外部類的強引用,所以我們將內部類聲明成靜態的就可以了。

void createInnerClass() { static class InnerClass { } inner = new InnerClass(); }
 

內存泄漏3:靜態Drawable

當一個Drawable附加到一個 View上時,
View會將其作為一個callback設定到Drawable上。意味着Drawable擁有一個View的引用,上面說了view會有上下文的引用

 

 

內存泄漏4:靜態集合中對象沒清理造成的內存泄漏

我們通常把一些對象的引用加入到了集合中,當我們不需要該對象時,並沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。

 

 

內存泄漏5:單例導致內存泄露

單例模式在Android開發中會經常用到,但是如果使用不當就會導致內存泄露。因為單例的靜態特性使得它的生命周期同應用的生命周期一樣長,如果一個對象已經沒有用處了,但是單例還持有它的引用,那么在整個應用程序的生命周期它都不能正常被回收,從而導致內存泄露。

public class AppSettings { private static AppSettings sInstance; private Context mContext; private AppSettings(Context context) { this.mContext = context; } public static AppSettings getInstance(Context context) { if (sInstance == null) { sInstance = new AppSettings(context); } return sInstance; } } 

像上面代碼中這樣的單例,如果我們在調用getInstance(Context context)方法的時候傳入的context參數是ActivityService等上下文,就會導致內存泄露。

Activity為例,當我們啟動一個Activity,並調用getInstance(Context context)方法去獲取AppSettings的單例,傳入Activity.this作為context,這樣AppSettings類的單例sInstance就持有了Activity的引用,當我們退出Activity時,該Activity就沒有用了,但是因為sIntance作為靜態單例(在應用程序的整個生命周期中存在)會繼續持有這個Activity的引用,導致這個Activity對象無法被回收釋放,這就造成了內存泄露。

為了避免這樣單例導致內存泄露,我們可以將context參數改為全局的上下文:

private AppSettings(Context context) { this.mContext = context.getApplicationContext(); } 

全局的上下文Application Context就是應用程序的上下文,和單例的生命周期一樣長,這樣就避免了內存泄漏。

單例模式對應應用程序的生命周期,所以我們在構造單例的時候盡量避免使用Activity的上下文,而是使用Application的上下文。





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM