老李分享:Android性能优化之内存泄漏

 

前言

对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我们可以借助LeakCanary、MAT等工具来检测应用程序是否存在内存泄漏,MAT是一款强大的内存分析工具,功能繁多而复杂,而LeakCanary则是由Square开源的一款轻量第三方内存泄漏检测工具,当它检测到程序中有内存泄漏的产生时,它将以最直观的方式告诉我们该内存泄漏是由谁产生的和该内存泄漏导致谁泄漏了而不能回收,供我们复查。

内存泄漏

为什么会产生内存泄漏?

当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。

内存泄漏对程序的影响?

内存泄漏是造成应用程序OOM的主要原因之一!我们知道Android系统为每个应用程序分配的内存有限,而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash。

Android中常见的内存泄漏汇总

单例造成的内存泄漏

单例模式非常受开发者的喜爱,不过使用的不恰当的话也会造成内存泄漏,由于单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。 
如下这个典例:

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;

}

}

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要: 
1、传入的是ApplicationContext这将没有任何问题,因为单例的生命周期和Application的一样长 
2、传入的是ActivityContext当这个Context所对应的Activity退出时,由于该Context和Activity的生命周期一样长(Activity间接继承于Context),所以当前Activity退出时它的内存并不会被回收,因为单例对象持有该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的引用,导致Activity的内存资源不能正常回收。正确的做法为: 
将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请使用ApplicationContext

Handler造成的内存泄漏

Handler的使用造成的内存泄漏问题应该说最为常见了,平时在处理网络任务或者封装一些请求回调等api都应该会借助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);

}

}

老李分享:Android性能优化之内存泄漏1的更多相关文章

  1. Android 性能优化之内存泄漏检测以及内存优化(中)

    https://blog.csdn.net/self_study/article/details/66969064 上篇博客我们写到了 Java/Android 内存的分配以及相关 GC 的详细分析, ...

  2. 老李分享:Android性能优化之内存泄漏3

    线程造成的内存泄漏 对于线程造成的内存泄漏,也是平时比较常见的,如下这两个示例可能每个人都这样写过: //——————test1 new AsyncTask<Void, Void, Void&g ...

  3. 老李分享:Android性能优化之内存泄漏2

    这种创建Handler的方式会造成内存泄漏,由于mHandler是Handler的非静态匿名内部类的实例,所以它持有外部类Activity的引用,我们知道消息队列是在一个Looper线程中不断轮询处理 ...

  4. [Android 性能优化系列]内存之基础篇--Android怎样管理内存

    大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...

  5. [Android 性能优化系列]内存之提升篇--应用应该怎样管理内存

    大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地 ...

  6. Android性能优化之内存篇

    下面是内存篇章的学习笔记,部分内容与前面的性能优化典范有重合,欢迎大家一起学习交流! 1)Memory, GC, and Performance 众所周知,与C/C++需要通过手动编码来申请以及释放内 ...

  7. Android性能优化之内存优化练习

    练习题目地址:https://github.com/lzyzsd/MemoryBugs 分析及优化过程如下: 问题1 静态变量引用activity 使用神器LeakCanary检查内存泄露问题 从图中 ...

  8. 35、Android 性能优化、内存优化

    http://blog.csdn.net/a_asinceo/article/details/8222104 http://blog.csdn.net/a_asinceo/article/detail ...

  9. Android性能优化之常见的内存泄漏

    前言 对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我们可以借助LeakCanary.MAT等工具来检 ...

随机推荐

  1. Extjs mvc

    MVC的模式,模型(Models)和控制器(Controllers) Model模型 是字段和它们的数据的集合,例如User模型带有username和password字段,模型知道如何持久化自己的数据 ...

  2. Vue.js 系列教程 5:动画

    原文:intro-to-vue-5-animations 译者:nzbin 译者的话:经过两周的努力,终于完成了这个系列的翻译,由于时间因素及个人水平有限,并没有详细的校对,其中仍然有很多不易理解的地 ...

  3. 安居客Android项目架构演进

    入职安居客三年从工程师到Team Leader,见证了Android团队一路走来的发展历程.因此有心将这些记录下来与大家分享,也算是对自己三年来一部分工作的总结.希望对大家有所帮助,更希望能得到大家宝 ...

  4. IntelliJ IDEA 报错:Error:java: 未结束的字符串文字

    构建javaweb项目时,控制台报错: 这个问题是由于编码冲突导致的,在设置中找到File Encodings可以看到 ide采用utf-8编码格式,而项目则是GBK编码方式,由此产生冲突. 解决办法 ...

  5. 依赖ConstraintLayout报错,Could not find *****,Failed to resolve:*****

    ConstraintLayout 约束布局,AndroidStudio2.2中新增功能之一,可以先去看看这篇文章 Android新特性介绍,ConstraintLayout完全解析,2.3版本的And ...

  6. Java中代理对象的使用小结

    在某些情况下,一个客户不想或不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到了中介作用,这不仅仅使用代理模式,还可以实现适配器模式.装饰模式等. 代理对象内部含有对真实对象的引用,从而 ...

  7. Jquery 客户端生成验证码

    验证码的作用: 1.有效防止这种问题对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上是用验证码是现在很多网站通行的方式(比如招商银行的网上个人银行,腾讯的QQ社区),我们利用比较简 ...

  8. KoaHub.js:使用ES6/7特性开发Node.js框架

    KoaHub.js -- 基于 Koa.js 平台的 Node.js web 快速开发框架.可以直接在项目里使用 ES6/7(Generator Function, Class, Async & ...

  9. 算法模板——平衡树Treap

    实现功能如下——1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大 ...

  10. osprofiler在openstack Cinder里的使用

    最近在做OpenStack Cinder driver的性能调试, 之前一直是通过在driver里面加入decorator,完成driver各个接口的执行时间的统计. 其实在openstack,已经在 ...