內(nèi)存泄漏
內(nèi)存泄漏就是分配的內(nèi)存空間沒有及時回收導(dǎo)致的肉微。可使用的內(nèi)存變少敬拓,應(yīng)用變卡老速,最后內(nèi)存溢出后應(yīng)用就會掛掉
內(nèi)存泄漏的檢測
建議閱讀Android內(nèi)存泄漏檢測和定位這篇文章,使用里面的檢測方法可以輕松的驗(yàn)證本文中的內(nèi)存泄漏例子
原因
Android內(nèi)存泄漏大多是因?yàn)锳ctivity沒有被回收導(dǎo)致的畏腕,Activity沒有被回收一般分為兩種情況
- 全局的static變量持有Activity的強(qiáng)引用
- 在Activity生命周期外的線程缴川,持有Activity的強(qiáng)引用
引用類型
推薦閱讀Java中四種引用類型,感覺是非常容易理解的
靜態(tài)變量
類中定義了靜態(tài)Activity變量描馅,把當(dāng)前的Activity賦值給靜態(tài)變量把夸,如果Activity生命周期結(jié)束的時候靜態(tài)變量沒有清空,就會導(dǎo)致內(nèi)存泄漏铭污。static變量是貫穿整個應(yīng)用的生命周期的恋日,所以被泄漏的Activity就會一直存在于應(yīng)用的進(jìn)程中,不會被回收嘹狞,同樣的持有Activity(Context)的靜態(tài)變量岂膳,比如View
也是一樣的道理
private static Activity sActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
sActivity = this;
findViewById(R.id.btn_back).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
內(nèi)部類
非靜態(tài)內(nèi)部類 和 匿名類 都會潛在的引用它們所屬的外部類,但是靜態(tài)內(nèi)部類卻不會磅网。
private static Test sTest;
private static Test2 sTest2;
class Test { }
static class Test2 { }
private void test() {
sTest = new Test();
sTest2=new Test2();
}
1.如果這個非靜態(tài)內(nèi)部類實(shí)例內(nèi)部做了一些耗時的操作谈截,就會導(dǎo)致外圍對象不會被回收,從而導(dǎo)致內(nèi)存泄漏
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
private void test() {
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
這個匿名內(nèi)部類會引用這個Activity涧偷,內(nèi)部開了線程做耗時操作簸喂,就會導(dǎo)致這個Activity不能被回收
2.結(jié)合上面的靜態(tài)變量,如果靜態(tài)變量持有非靜態(tài)內(nèi)部類的引用嫂丙,而
非靜態(tài)內(nèi)部類引用了該Activity娘赴,那就會導(dǎo)致這個Activity不能被回收
private static Test sTest;
class Test {}
private void test() {
sTest = new Test();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test();
}
Test
是非靜態(tài)內(nèi)部類,sTest
是靜態(tài)變量跟啤,注意這個靜態(tài)變量是在onCreate
中初始化的诽表,會持有該Activity的引用,Activity被銷毀的時候sTest
不置空那么該Activity
就無法被回收隅肥;
內(nèi)部類常見情況
上面都說了竿奏,非靜態(tài)內(nèi)部類會引用所屬外部類,這時候如果創(chuàng)建一個內(nèi)部類腥放,而且持有一個靜態(tài)變量的引用就容易會引起外部類沒法被回收泛啸;同樣的如果該內(nèi)部類在子線程做了一些耗時操作,屬于在Activity生命周期外的線程秃症,也會導(dǎo)致外部類沒法被回收候址;常見的情況有下面幾種
Threads
上面第一個例子已經(jīng)寫過了
TimerTask
匿名內(nèi)部類嘛吕粹,肯定就持有所在Activity
的引用,又做耗時操作岗仑,肯定內(nèi)存泄漏
Handler
一個道理匹耕,匿名內(nèi)部類嘛,肯定就持有所在Activity
的引用荠雕,如果執(zhí)行postDelayed
的時候稳其,Activity被銷毀,那么Handler
持有的Activity沒法被回收炸卑,就內(nèi)存泄漏了既鞠,而且里面也有一個匿名內(nèi)部類Runnable
持有Activity
new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
}.postDelayed(new Runnable() {
@Override
public void run() {
}
},10000);
系統(tǒng)服務(wù)
這個也很好理解,當(dāng)你使用系統(tǒng)服務(wù)的時候盖文,可以注冊監(jiān)聽器嘱蛋,會導(dǎo)致服務(wù)持有Context
的引用,如果在Activity銷毀的時候椅寺,沒有注銷掉監(jiān)聽器浑槽,就會導(dǎo)致內(nèi)存泄漏;
//傳感器的監(jiān)聽器注冊
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
注冊廣播接收器也是需要在onDestroy
方法里面注銷
資源未釋放
這個簡單返帕,一般就各種流桐玻、各種資源沒有關(guān)閉,集合中對象沒清理導(dǎo)致荆萤,倒是不容易犯錯
靜態(tài)內(nèi)部類解決內(nèi)存泄漏
1.靜態(tài)內(nèi)部類不會持有外部類的引用镊靴,所以使用靜態(tài)內(nèi)部類可以解決以上問題,如果靜態(tài)內(nèi)部類里面需要引用外部類链韭,可以通過弱引用的方式來引用偏竟;
2.用static的變量引用匿名內(nèi)部類的實(shí)例或?qū)⒛涿麅?nèi)部類的實(shí)例化操作放到外部類的靜態(tài)方法中
//靜態(tài)內(nèi)部類
private static class Myhandler extends Handler {
private final WeakReference<Activity> mActivity;
public Myhandler(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Activity activity = mActivity.get();
// ...
}
}
private final Myhandler mMyhandler=new Myhandler(this);
//這樣寫不會持有外部類的引用
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyhandler.postDelayed(sRunnable, 1000 * 60 * 10);
}
方法沒有問題,但是為什么上面的寫法sRunnable
沒有引用外部類而下面的寫法會引用呢敞峭,會導(dǎo)致內(nèi)存泄漏呢踊谋;我覺得是因?yàn)槌跏蓟奈恢貌煌o態(tài)變量和靜態(tài)類先被初始化了旋讹,所以沒有外部類的引用殖蚕;感覺是這樣的吧
private static Runnable sRunnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
}
參考文章: