背景
在Android開(kāi)發(fā)中內(nèi)存泄漏是一個(gè)相對(duì)來(lái)說(shuō)比較常見(jiàn)的問(wèn)題,這個(gè)問(wèn)題也相當(dāng)嚴(yán)重,但是有好多朋友還不知道怎么解決和查看內(nèi)存泄漏問(wèn)題,這里就寫(xiě)一篇文章來(lái)給大家介紹一些常見(jiàn)的內(nèi)存泄漏問(wèn)題以及解決方法,如果想看內(nèi)存泄漏檢測(cè)的可以看這里 Android內(nèi)存泄漏檢測(cè)。
常見(jiàn)內(nèi)存泄漏
1. 靜態(tài)引用
比如以下代碼,定義了sInstance來(lái)傳遞和使用,會(huì)導(dǎo)致MainActivity無(wú)法被銷毀,這是一種比較低級(jí)的錯(cuò)誤,一般我們不建議這么使用,如果一定要使用,就需要在最后將sInstance置空。
public class MainActivity extends Activity {
private static Activity sInstance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sInstance = this;
}
@Override
protected void onDestroy() {
super.onDestroy();
sInstance = null;
}
}
2. 內(nèi)部類持有外部類的引用,內(nèi)部類被靜態(tài)引用,等同于外部類被靜態(tài)引用
在JAVA中內(nèi)部類的定義與使用一般為成員內(nèi)部類與匿名內(nèi)部類,他們的對(duì)象都會(huì)隱式持有外部類對(duì)象的引用,影響外部類對(duì)象的回收。
GC只會(huì)回收沒(méi)有被引用或者根集不可到達(dá)的對(duì)象(取決于GC[算法]),內(nèi)部類在生命周期內(nèi)始終持有外部類的對(duì)象的引用,造成外部類的對(duì)象始終不滿足GC的回收條件,反映在內(nèi)存上就是內(nèi)存泄露。(如,[Android]中Activity的內(nèi)存泄露)
解決方案為
1.將內(nèi)部類定義為static
2.用static的變量引用匿名內(nèi)部類的實(shí)例或?qū)⒛涿麅?nèi)部類的實(shí)例化操作放到外部類的靜態(tài)方法中
public class MainActivity extends Activity {
private static TestClass sTestClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
public class TestClass{
}
}
3. AsyncTask
下面的代碼我們?cè)谕顺龅臅r(shí)候mAsyncTask是無(wú)法被銷毀的,會(huì)導(dǎo)致Activity無(wú)法被銷毀:
private AsyncTask mAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAsyncTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
while (true);
}
};
mAsyncTask.execute();
}
解決辦法如下代碼:在Activity銷毀的時(shí)候取消正在運(yùn)行的AsyncTask
public class MainActivity extends Activity {
private AsyncTask mAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAsyncTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
while (true){
if(isCancelled()){
break;
}
}
return null;
}
};
mAsyncTask.execute();
}
@Override
protected void onDestroy() {
super.onDestroy();
if(null != mAsyncTask && !mAsyncTask.isCancelled()){
mAsyncTask.cancel(true);
}
mAsyncTask = null;
}
}
4. Handle造成的內(nèi)存泄漏
4.1 用過(guò)內(nèi)部類創(chuàng)建的Handler對(duì)象,持有Activity的引用。當(dāng)執(zhí)行postDelayed()方法時(shí),會(huì)把Handler裝入一個(gè)Message,并把Message推到MessageQueue中,MessageQueue在一個(gè)Looper線程中不斷輪詢處理消息。
因?yàn)檠舆t的時(shí)間足夠長(zhǎng),當(dāng)Activity退出時(shí),消息隊(duì)列還未處理Message,從而引發(fā)OOM
public class MainActivity extends Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.sendEmptyMessageDelayed(0,1000);
}
}
那么對(duì)于上面產(chǎn)生的內(nèi)存泄漏我們要怎么處理呢?采用弱引用的形式:
public class MainActivity extends Activity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler(new WeakReference<Activity>(this));
mHandler.sendEmptyMessageDelayed(0,1000);
}
public static class MyHandler extends Handler{
private WeakReference<Activity> mActivity;
public MyHandler(WeakReference<Activity> activityWeakReference){
mActivity = activityWeakReference;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(null != mActivity){
Activity activity = mActivity.get();
if(null != activity && !activity.isFinishing()){
//TODO do something...
}
}
}
}
}
4.2 內(nèi)部調(diào)用Handler,解決辦法,在Activity銷毀的時(shí)候?qū)andler里面的信息清除。
public class MainActivity extends Activity {
private Handler mHandler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
}
},1000);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
5. Thread引起的內(nèi)存泄漏
下面的方法會(huì)產(chǎn)生內(nèi)存泄漏,相信大概都知道,那么我們應(yīng)該怎么去解決呢?
public class MainActivity extends Activity {
private Thread mThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThread = new Thread(){
@Override
public void run() {
super.run();
while (true){
}
}
};
mThread.start();
}
}
通過(guò)前面幾種內(nèi)存泄漏的解決方法,我相信大家應(yīng)該也能猜出個(gè)大概,無(wú)非就是在Activity銷毀的時(shí)候去暫停Thread那么我們應(yīng)該怎么做呢?
@Override
protected void onDestroy() {
super.onDestroy();
if(null != mThread){
mThread.interrupt();
}
}
interrupt()和Thread.interrupt()的區(qū)別,其中interrupt()是作用于調(diào)用線程的,比如我們上面調(diào)用的,他是作用于mThread這個(gè)線程的,如果我們?cè)谏厦媸褂肨hread.interrupt()那么就是作用于主線程的。
6. WebView造成的內(nèi)存泄漏
WebView在解析網(wǎng)頁(yè)時(shí),會(huì)申請(qǐng)Native堆內(nèi)存,保存頁(yè)面元素(圖片、history等)
7. 其他原因
BraodcastReceiver
ContentObserver
File
Cursor
Stream
Bitmap
Timer
總結(jié)
上面是我在平時(shí)開(kāi)發(fā)中遇到的一些關(guān)于內(nèi)存泄漏的地方,大家有什么其他的內(nèi)存泄漏或者對(duì)于以上有更好的解決方法,歡迎留言。樓主還寫(xiě)了一篇關(guān)于 Android內(nèi)存泄漏檢測(cè)的有興趣的同學(xué)也可以看一下。