Android 常见内存泄漏及解决方法

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 
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/414646.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

二、内聚

【内聚】 1.内聚的定义 一个模块内部元素彼此结合的紧密程度。其中关键字&#xff1a;模块、元素、结合 模块&#xff1a;大到子系统&#xff0c;小到一个函数&#xff0c;都可以看成一个模块 元素&#xff1a; 模块&#xff1a;包、命名空间等 包&#xff1a;类、接口、全局数…

前端学习(2733):重读vue电商网站43之使用 lodash 中 cloneDeep(obj) 来实现深拷贝

lodash 官方文档传送门 Using npm Js $ npm i -g npm $ npm i --save lodash_.cloneDeep(value) 使用 项目中使用 cloneDeep 需求&#xff1a;由于 addForm 中商品所属的分类数组 goods_cat在商品分类模块需要使用 v-model 进行双向绑定数组形式&#xff0c;而我们调用添加商品…

Android Runnable与Handler和Thread的使用,Handler构造方法弱引用实现

PS&#xff1a;在使用Runnable时&#xff0c;可能会内存泄露。Runnable是一个匿名内部类&#xff0c;因此它对当前Activity有一个隐式引用。如果Activity在销毁之前&#xff0c;任务还未完成&#xff0c; 那么将导致Activity的内存资源无法回收&#xff0c;造成内存泄漏。那么该…

Spring Data JPA框架

1、前言 扔一个 spring data jpa 的代码&#xff0c;可运行&#xff0c;后续补充博客内容。 环境&#xff1a;eclipse tomcat8 2、部分截图 3、源码 https://gitee.com/niceyoo/jpa-demo 4、sql 既然都是 spring data jpa &#xff0c;还要啥数据库呀&#xff0c;自己创建一个…

jdk1.7 String switch的实现

对于int的switch&#xff0c;jvm是用tableswitch和lookupswitch来实现的&#xff0c;jdk1.7 switch增加了对string的支持&#xff0c;那么底层是如何实现的呢&#xff1f;是否增加了新的指令或是否给某些指令增加了新的含义&#xff1f; 看这样一个程序&#xff1a; Java代码 …

Android 多线程实现异步执行demo,线程池使用demo

方法1&#xff1a; 1、常见Runnable对象设置同步代码run运行体 class AutoSaleTicket implements Runnable {private int ticket 20;public void run() {while (true) {// 循环是指线程不停的去卖票// 当操作的是共享数据时,用同步代码块进行包围起来,这样在执行时,只能有一…

Mvc5 控制器,视图简单说明

本系列会比Mvc4更详细。Mvc4记录或没记录的东西这里也会提到。 控制器 自动装配&#xff1a; 一般自动装配对于添加的时候比较好用 视图&#xff1a; 控制器返回的视图&#xff0c;其实就是一些静态的HTML。动态性不好&#xff0c;从控制器传递少量数据&#xff0c;最简单使用…

前端学习(2734):重读vue电商网站44之使用 echarts

echarts 官方文档传送门 根据官方文档示例&#xff0c;我们先展示测试数据。 Javascript <template><div><!-- 面包屑导航区域 --><el-breadcrumb separator"/"><el-breadcrumb-item :to"{ path: /home }">首页</el-bre…

No fallback instance of type class found for feign client user-service(转)

No fallback instance of type class found for feign client user-service&#xff08;转&#xff09; 1、错误日志 在 feign 开启熔断&#xff0c;配置 fallback 类&#xff0c;实现当前接口的实现类时&#xff0c;报错信息如下&#xff1a; Error starting ApplicationCont…

Redis在windows下安装过程

https://www.cnblogs.com/M-LittleBird/p/5902850.html 一、下载windows版本的Redis 去官网找了很久&#xff0c;发现原来在官网上可以下载的windows版本的&#xff0c;现在官网以及没有下载地址&#xff0c;只能在github上下载&#xff0c;官网只提供linux版本的下载 官网下载…

前端学习(2735):重读vue电商网站45之项目优化上线

优化Vue项目&#xff0c;部署Vue项目 项目优化 添加进度条 nprogress 进度条官方文档传送门 打开 vue-ui 面板&#xff0c;选择依赖&#xff0c;输入 nprogress 进行安装相关依赖。 npm Javascript npm install --save nprogress在 main.js 入口文件中&#xff0c;进行相关…

Push rejected: Push to origin/master was rejected

Push rejected: Push to origin/master was rejected 1、错误日志 23:04 Maven projects need to be imported: Import Changes Enable Auto-Import23:05 4 files committed: 初始化项目23:06 Push rejected: Push to origin/master was rejected23:07 2 files …

基本卷-动态卷性能测评(未完待续)

320G 同一磁盘复制1G 1个文件 //基本磁盘 27秒 FastCopy 1.62 128M缓存 37秒 Windows自带复制 //动态卷 15秒 Windows自带复制 27秒 FastCopy 1.62 128M缓存 28秒 FastCopy 1.62 32M缓存 320G 同一磁盘复制1G 4470个文件 //动态卷 54秒 Windows自带复制 考虑是否是磁盘的原因 3…

前端学习(2736):重读vue电商网站46之执行build 时报错

Error: No module factory available for dependency type: CssDependency 解决办法&#xff1a; 参考&#xff1a;解决webpack打包报错 No module factory available for dependency type: CssDependency 在执行 build 命令期间移除所有的 console babel-plugin-transform…

Android 获取光线强弱,开启闪光灯,关闭闪光灯

1、获取光线强度 &#xff08;1&#xff09;实现传感器接口 implements SensorEventListener 实例化 private SensorManager mSensroMgr;//传感器管理类 mSensroMgr (SensorManager) getSystemService(Context.SENSOR_SERVICE); &#xff08;2&#xff09;监听传感器 Ove…

Python 文件 fileno() 方法

描述 Python 文件 fileno() 方法返回一个整型的文件描述符(file descriptor FD 整型)&#xff0c;可用于底层操作系统的 I/O 操作。 语法 fileno() 方法语法如下&#xff1a; fileObject.fileno() 参数 无 返回值 返回文件描述符。 实例 以下实例演示了 fileno() 方法的使用&am…

@RequestParam @PathVariable

RequestParam PathVariable 1、Request参数 在访问各种各样网站时&#xff0c;经常会发现网站的URL的最后一部分形如&#xff1a;&#xff1f;xxxxyyyy&zzzzwwww。这就是HTTP协议中的Request参数&#xff0c;它有什么用呢&#xff1f;先来看一个例子&#xff1a; 在知乎中…

前端学习(2737):重读vue电商网站47之生成打包报告

打包时&#xff0c;为了直观地发现项目中存在的问题&#xff0c;可以在打包时生成报告。生成报告的方式有两种&#xff1a; ① 通过命令行参数的形式生成报告 Javascript // 通过 vue-cli 的命令选项可以生成打包报告 // --report 选项可以生成 report.html 以帮助分析包内容 …

蓄水池抽样算法 Reservoir Sampling

2018-03-05 14:06:40 问题描述&#xff1a;给出一个数据流&#xff0c;这个数据流的长度很大或者未知。并且对该数据流中数据只能访问一次。请写出一个随机选择算法&#xff0c;使得数据流中所有数据被选中的概率相等。 问题求解&#xff1a;如果是长度已知或者有限的问题&…