Android中的内存泄漏:

先说一下为什么会出现内存泄漏:

Android程序开发中,如果一个对象已经不需要被使用了,本该被回收时,而这时另一个对象还在持有对该对象的引用,这样就会导致无法被GC回收,就会出现内存泄漏的情况。

内存泄漏时Android程序中出现OOM问题的主要原因之一。所以我们在编写代码时,一定要细心处理好这一类的问题。

下面说一下Android开发中最常见的5个内存泄漏问题:

一:单例设计模式造成的内存泄漏:

单例设计模式我就不多说了,这个是最基本的设计模式,相信大家都会使用,但是时候我们在使用单例设计模式时没有注意到其中的细节,就会造成内存泄漏。

单例设计模式的静态特性会使他的生命周期和应用程序的生命周期一样长,这就说明了如果一个对象不在使用了,而这时单例对象还在持有该对象的引用,这时GC就会无法回收该对象,造成了内存泄露的情况。

下面是错误的单例设计模式的代码:

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}
上面的代码是一个最普通的单例模式,但是需要注意两个问题:
1、如果我们传入的Context是Application的Context的话,就没有任何问题,因为Application的Context生命周期和应用程序生命周期一样长。

2、如果我们传入的Context是Activity的Context的话,这时如果我们因为需求销毁了该Activity的话,Context也会随着Activity被销毁,但是单例还在持有对该类对象的引用,这时就会造成内存泄漏。

所以,正确的单例模式写法应该是这样的:

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context.getApplicationContext();
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}
这样的话不管我们传入什么样的Context,最终使用的都是Application的Context,单例的生命周期和应用一样长,这样就不会造成内存泄漏了。

二、非静态内部类创建的静态实例造成的内存泄漏

有时候因为需求我们会去频繁的启动一个Activity,这时为了避免频繁的创建相同的数据源,我们通常会做如下处理:

public class MainActivity extends AppCompatActivity {
 
    private static TestResource mResource = null;
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_main);
 
        if(mManager == null){
 
            mManager = new TestResource();
 
        }
 
        //...
 
    }
 
    class TestResource {
 
        //...
 
    }
 
}
这样就在Activity中创建了非静态内部类,非静态内部类默认持有Activity类的引用,但是他的生命周期还是和应用程序一样长,所以当Activity销毁时,静态内部类的对象引用不会被GC回收,就会造成了内存溢出,解决办法:

1、将内部类改为静态内部类。

2、将这个内部类封装成一个单例,Context使用Application的Context

三、Handler造成的内存泄漏:

先看一下不规范的Handler写法:

public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //...
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    private void loadData(){
        //...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
}
这里的handler也是一个非静态匿名内部类,他跟上面的一样,也会持有Activity的引用,我们知道handler是运行在一个Looper线程中的,而Looper线程是轮询来处理消息队列中的消息的,假设我们处理的消息有十条,而当他执行到第6条的时候,用户点击了back返回键,销毁了当前的Activity,这个时候消息还没有处理完,handler还在持有Activity的引用,这个时候就会导致无法被GC回收,造成了内存泄漏。正确的做法是:

public class MainActivity extends AppCompatActivity {
//new一个自定义的Handler
    private MyHandler mHandler = new MyHandler(this);
    private TextView mTextView ;
 
//自定义静态内部类继承自Handler
    private static class MyHandler extends Handler {
        private WeakReference<Context> reference;
//在构造函数中使用弱引用来引用context对象
        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if(activity != null){
                activity.mTextView.setText("");
            }
        }
    }
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView)findViewById(R.id.textview);
        loadData();
    }
  
    private void loadData() {
        //...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
 
@Override
  protected void onDestroy() {
      super.onDestroy();
//移除队列中所有的Runable和消息
//这里也可以使用mHandler.removeMessage和mHandler.removeCallBacks来移除指定的Message和Runable
      mHandler.removeCallbacksAndMessages(null);
  }
}

创建一个静态内部类继承自handler,然后再在构造参数中对handler持有的对象做弱引用,这样在回收时就会回收了handler持有的对象,这里还做了一处修改,就是当我
们的回收了handler持有的对向,即销毁了该Activity时,这时如果handler中的还有未处理的消息,我们就需要在OnDestry方法中移除消息队列中的消息。

四、线程造成的内存泄漏

线程使用不恰当造成的内存泄漏也是很常见的,下面举两个例子:
//——————test1
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                SystemClock.sleep(10000);
                return null;
            }
        }.execute();
//——————test2
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(10000);
            }
        }).start();
上面是两个内部类,当我们的Activity销毁时,这两个任务没有执行完毕,就会使Activity的内存资源无法被回收,造成了内存泄漏。
正确的做法是使用静态内部类:如下
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
        private WeakReference<Context> weakReference;
  
        public MyAsyncTask(Context context) {
            weakReference = new WeakReference<>(context);
        }
  
        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(10000);
            return null;
        }
  
        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            MainActivity activity = (MainActivity) weakReference.get();
            if (activity != null) {
                //...
            }
        }
    }
    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            SystemClock.sleep(10000);
        }
    }
//——————
    new Thread(new MyRunnable()).start();
    new MyAsyncTask(this).execute();
这样就避免了内存泄漏,当然在Activity销毁时也要记得在OnDestry中调用AsyncTask.cancal()方法来取消相应的任务。避免在后台运行浪费资源。

五、资源未关闭造成的内存泄漏

在使用完BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源时,一定要在Activity中的OnDestry中及时的关闭、注销或者释放内存,
否则这些资源不会被GC回收,就会造成内存泄漏。
---------------------

原文:https://blog.csdn.net/qq_35373333/article/details/74909811

Android 常见内存泄漏及解决方法相关推荐

  1. Android常见内存泄漏以及解决办法

    #1. 注意你的Context引用 ##尝试使用合适的context## (1).Toast能在许多的Activity看到, 使用 getApplicationContext() (2).servic ...

  2. Android之内存泄漏以及解决办法(持更)

    Android之内存泄漏以及解决办法 文章链接:http://blog.csdn.net/qq_16628781/article/details/67761590 知识点: 单例造成的内存泄漏原因和解 ...

  3. android 将图片储存到手机内存不足,Android手机内存不足的解决方法

    Android手机内存不足的解决方法 如果你的安卓手机内存不是很大,安装较多软件,使用一段时间后,安卓手机和容易引起内存不足,会提示手机内存不足,然后速度变慢,无法安装新软件等等.很多对安卓手机不太了 ...

  4. 来点干货 | Android 常见内存泄漏与优化(二)

    作者 | 无名之辈FTER 责编 | 夕颜 出品 | CSDN(ID:CSDNnews) 在昨天的Android 内存泄漏问题多多,怎么优化?一文中,我们详细阐述了Java虚拟机工作原理和Androi ...

  5. Android常见内存泄漏

    1.什么是内存泄露 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃(内存溢出OOM)等严重后果. 内存 ...

  6. Android常见内存泄漏及优化总结

    前言 最近在整理回顾零碎知识点,今天整理下Android内存优化方案分享给大家. 在Android开发中,一些不好的编程习惯会导致我们的开发的app存在内存泄露的情况.下面简单介绍一些在Android ...

  7. SqlServer 内存篇(五)—— 常见内存错误与解决方法

    内存错误与内存瓶颈不同,当出现内存错误时,轻则某些特殊操作不能完成,重则整个sqlserver无响应,对sqlserver影响通常很大. sqlserver常见的内存错误主要有以下三种: 701 -- ...

  8. android播放器内存不足,Android手机内存不足的解决方法

    如果你的安卓手机内存不是很大,安装较多软件,使用一段时间后,安卓手机和容易引起内存不足,会提示手机内存不足,然后速度变慢,无法安装新软件等等.很多对安卓手机不太了解的童鞋或多或少都会碰到这种情况.而安 ...

  9. Android性能优化之利用强大的LeakCanary检测内存泄漏及解决办法

    本篇文章主要介绍了Android性能优化之利用LeakCanary检测内存泄漏及解决办法,有兴趣的同学可以了解一下. 目录 前言 什么是内存泄漏? 内存泄漏造成什么影响? 什么是LeakCanary? ...

最新文章

  1. 使用带有用户名和密码的cURL?
  2. 001_Redis介绍
  3. 一位996、CRUD开发者的一天
  4. HTML/CSS——PC端QQ飞车官网首页
  5. JAVA面试题总汇(含答案)
  6. SAP Spartacus里的localStorage用法
  7. 网页设计html加音频,HTML5网页中如何嵌入音频,视频?
  8. 横向的二级导航菜单,在多浏览器下可用
  9. layui 点击头像 上传头像
  10. galleryview-3.0b3使用小记
  11. python cartopy绘制中国区域(包含国界、省界、十段线以及海南诸岛)
  12. ubuntu QT Creator Fatal IO error 2 (没有那个文件或目录) on X server :0
  13. android手机通过wifi控制数码管,淫技:android无屏操作之adb操控wifi
  14. python:初识自动化测试 playwright 库
  15. cocos2d-x学习笔记(持续更新)
  16. twitter点赞图标分析
  17. asp.net 生命周期
  18. 如何彻底卸载Oracle数据库
  19. 中科院北京自动化所考研信息//2021-01-25
  20. 在线获取访客QQ号码的原理及实现方法

热门文章

  1. [vue] v-model是什么?有什么用呢?
  2. 前端学习(2635):vs 需要插件下载
  3. 前端学习(2608):vuex的介绍
  4. 前端学习(2562):v-loading
  5. 前端学习(1919)vue之电商管理系统电商系统之用户角色权限的关系
  6. 前端学习(1802):前端调试之事件伪类
  7. 玩转oracle 11g(52):Oracle导出导入表(.sql、.dmp文件)两种方法
  8. java学习(22):if语句
  9. centos7网卡识别不到,无法远程工具连接
  10. Linux 给Qt应用软件创建图标启动