Android内存泄漏及解决方法

背景

在Android开发中内存泄漏是一个相对来说比较常见的问题,这个问题也相当严重,但是有好多朋友还不知道怎么解决和查看内存泄漏问题,这里就写一篇文章来给大家介绍一些常见的内存泄漏问题以及解决方法,如果想看内存泄漏检测的可以看这里 Android内存泄漏检测

常见内存泄漏

1. 静态引用

比如以下代码,定义了sInstance来传递和使用,会导致MainActivity无法被销毁,这是一种比较低级的错误,一般我们不建议这么使用,如果一定要使用,就需要在最后将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. 内部类持有外部类的引用,内部类被静态引用,等同于外部类被静态引用

在JAVA中内部类的定义与使用一般为成员内部类与匿名内部类,他们的对象都会隐式持有外部类对象的引用,影响外部类对象的回收。
GC只会回收没有被引用或者根集不可到达的对象(取决于GC[算法]),内部类在生命周期内始终持有外部类的对象的引用,造成外部类的对象始终不满足GC的回收条件,反映在内存上就是内存泄露。(如,[Android]中Activity的内存泄露)
解决方案为
1.将内部类定义为static
2.用static的变量引用匿名内部类的实例或将匿名内部类的实例化操作放到外部类的静态方法中

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

下面的代码我们在退出的时候mAsyncTask是无法被销毁的,会导致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);
            }
        };
        mAsyncTask.execute();
    }

解决办法如下代码:在Activity销毁的时候取消正在运行的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造成的内存泄漏

4.1 用过内部类创建的Handler对象,持有Activity的引用。当执行postDelayed()方法时,会把Handler装入一个Message,并把Message推到MessageQueue中,MessageQueue在一个Looper线程中不断轮询处理消息。
因为延迟的时间足够长,当Activity退出时,消息队列还未处理Message,从而引发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);
    }
}

那么对于上面产生的内存泄漏我们要怎么处理呢?采用弱引用的形式:

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 内部调用Handler,解决办法,在Activity销毁的时候将Handler里面的信息清除。

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引起的内存泄漏

下面的方法会产生内存泄漏,相信大概都知道,那么我们应该怎么去解决呢?

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();
    }
}

通过前面几种内存泄漏的解决方法,我相信大家应该也能猜出个大概,无非就是在Activity销毁的时候去暂停Thread那么我们应该怎么做呢?

@Override
    protected void onDestroy() {
        super.onDestroy();
        if(null != mThread){
            mThread.interrupt();
        }
    }

interrupt()和Thread.interrupt()的区别,其中interrupt()是作用于调用线程的,比如我们上面调用的,他是作用于mThread这个线程的,如果我们在上面使用Thread.interrupt()那么就是作用于主线程的。

6. WebView造成的内存泄漏

WebView在解析网页时,会申请Native堆内存,保存页面元素(图片、history等)

7. 其他原因

BraodcastReceiver
ContentObserver
File
Cursor
Stream
Bitmap
Timer

总结

上面是我在平时开发中遇到的一些关于内存泄漏的地方,大家有什么其他的内存泄漏或者对于以上有更好的解决方法,欢迎留言。楼主还写了一篇关于 Android内存泄漏检测的有兴趣的同学也可以看一下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,...
    宇宙只有巴掌大阅读 2,329评论 0 12
  • Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏...
    apkcore阅读 1,193评论 2 7
  • 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,...
    DreamFish阅读 775评论 0 5
  • Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏...
    _痞子阅读 1,586评论 0 8
  • 最近搞游戏的项目,结识了不少新的朋友外对游戏圈有了一些认识。 可以这么说,在iphone推出来的头几年我还是玩了一...
    ouzar阅读 228评论 0 0