性能优化——内存泄漏(3)代码分析篇

原创 2017年06月30日 17:10:38

内存泄漏系列文章:
性能优化——内存泄漏(1)入门篇
性能优化——内存泄漏(2)工具分析篇
性能优化——内存泄漏(3)代码分析篇

一、简述

在上一篇《性能优化——内存泄漏(2)工具分析篇》中,介绍了如何使用工具帮助我们检查APP中是否存在内存泄漏、及如何定位到内存泄漏,但项目并不能完全依赖工具来检查,毕竟定位内存泄漏比较麻烦,还不如在开发时就考虑到内存泄漏问题,尽可能减少内存泄漏,后续优化才不会那么痛苦。下面就来看看开发中,哪些代码可能造成内存泄漏,及避免内存泄漏的对应解决方案。

二、代码分析

1、静态变量引起的内存泄露

1)错误示例

这个可以拿之前的Demo来说明,Demo代码如下:

// 单例工具类
public class CommonUtil {
    private static CommonUtil mInstance;
    private Context mContext;
    public CommonUtil(Context context) {
        mContext = context;
    }
    public static CommonUtil getInstance(Context context) {
        if (mInstance == null) {
            synchronized (CommonUtil.class) {
                if (mInstance == null) {
                    mInstance = new CommonUtil(context);
                }
            }
        }
        return mInstance;
    }
    ...
}

// Activity中使用单例工具
public class MemoryLeakActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_leak);
        CommonUtil.getInstance(this);
    }
}

当调用getInstance()时,如果传入的context是Activity,那么只要这个单例没有被释放,则这个Activity也不会被释放,直到进程退出后才会释放。

2)解决方案

不要传入Activity,可以使用getApplicationContext()来代替。

2、非静态内部类引起内存泄露(包括匿名内部类)

在Java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用。

1)错误示例

public class MemoryLeakActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_leak);
        loadData();
    }

    public void loadData() {
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10000);
                    System.out.println("模拟同步网络数据完毕");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

上面的代码中,使用Thread匿名内部类开辟线程同步网络数据,而这个内部类会隐式持有外部类的引用,当退出界面后,该内部类任务还在进行,导致该界面无法被GC回收,于是就会产生内存泄漏。

APP退出后,执行GC,获取内存快照。可以看到MemoryLeakActivity的Total Count为1,说明存在内存泄漏。

2)解决方案

静态的内部类不会持有外部类的引用。

(1) 使用静态方法

public static void loadData() {
    new Thread() {
        @Override
        public void run() {
            try {
                Thread.sleep(10000);
                System.out.println("模拟同步网络数据完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }.start();
}

(2) 使用静态内部类

public void loadData() {
    new MyThread().start();
}

static class MyThread extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(10000);
            System.out.println("模拟同步网络数据完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3)拓展

但项目开发中,可能会存在一定要使用内部类去持有外部类的情况,比如数据同步完成后,需要修改界面上的文本信息,而静态内部类无法直接持有外部类的引用,这又该怎么解决呢?其实可以通过内部类的构造函数将外部类传入,并使用弱引用保存(GC执行后释放),并在内部类中做好判空即可。

static class MyThread extends Thread {
    Reference<Context> mReference;

    public MyThread(Context context) {
        mReference = new WeakReference<>(context);
    }

    @Override
    public void run() {
        try {
            Thread.sleep(10000);
            MemoryLeakActivity context = (MemoryLeakActivity) mReference.get();
            if (context != null) {
                context.mTv.setText("模拟同步网络数据完毕");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

APP退出后,执行GC,获取内存快照。可以看到MemoryLeakActivity的Total Count为0,说明没有内存泄漏。

3、不需要用的监听未移除会发生内存泄露

1)错误示例

public class MemoryLeakActivity extends AppCompatActivity implements SensorEventListener {

    private SensorManager mSm;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_memory_leak);

        mSm = (SensorManager) getSystemService(SENSOR_SERVICE);
        Sensor sensor = mSm.getDefaultSensor(Sensor.TYPE_ALL);
        mSm.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
}

本例中使用getSystemService()获取传感器服务,并设置了监听,但没有在onDestroy()方法中将监听移除,此类监听没有及时清除的话,必定造成内存泄漏。

2)解决方案

只需在onDestroy()中移除监听即可。

@Override
protected void onDestroy() {
    super.onDestroy();
    mSm.unregisterListener(this);
}

4、资源未关闭引起的内存泄露情况

1)错误示例

比如:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性。
当不需要使用的时候,要记得及时释放资源。否则就会内存泄露。

2)解决方案

这里以Cursor、IO流和自定义属性为例。

(1)Cursor或IO流

try {
    ...
    使用cursor/io读取数据操作
    ...
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (cursor/io != null) {
        cursor/io.close();
        cursor/io = null;
    }
}

(2)自定义属性

TypedArray a = theme.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
TypedArray appearance = null;
int ap = a.getResourceId(
        com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
a.recycle();// 不执行回收会造成内存泄漏
版权声明:本文为博主原创文章,未经博主允许不得转载。

ECharts源码分析优化

一、内存优化的原因及解决方案 ECharts在现代浏览器(IE9+、Chrome)下长时间使用,内存的变化并不特别明显。但在IE 8 或以下浏览器长期使用时,则出现内存不断增大的情况。其主要原因是EC...
  • u011155228
  • u011155228
  • 2015年06月01日 17:10
  • 5018

Android 性能优化之使用MAT分析内存泄露问题

我们平常在开发Android应用程序的时候,稍有不慎就有可能产生OOM,虽然JAVA有垃圾回收机,但也不能杜绝内存泄露,内存溢出等问题,随着科技的进步,移动设备的内存也越来越大了,但由于Android...
  • xiaanming
  • xiaanming
  • 2015年01月09日 08:50
  • 33155

6. GC 调优(工具篇) - GC参考手册

进行GC性能调优时, 需要明确了解, 当前的GC行为对系统和用户有多大的影响。有多种监控GC的工具和方法, 本章将逐一介绍常用的工具, 以及如何获取原生数据。 后续的章节将对重要的派生指标(deri...
  • renfufei
  • renfufei
  • 2017年02月23日 18:56
  • 3896

性能优化——内存泄漏(2)工具分析篇

一、简述在上一篇《性能优化——内存泄漏(1)入门篇》中,介绍了内存泄漏的基本概念,并举了一个Demo,结合简单的代码分析,猜测出Demo中存在内存泄漏,并用Android Studio自带的Memor...
  • CSDN_LQR
  • CSDN_LQR
  • 2017年06月30日 14:52
  • 912

Xcode禁用代码分析的警告和内存泄漏

在使用xcode进行iphone应用开发时,经常需要添加一些第三方的类库,而一些第三方的类库由于缺少维护,从而导致类库中含有各种警告和各种内存泄漏,但并不影响运行. 倘若我们需要用到第三方库,而...
  • langyapojun
  • langyapojun
  • 2014年07月11日 14:32
  • 1044

xcode禁用代码分析的警告和内存泄漏

在使用xcode进行iphone应用开发时,经常需要添加一些第三方的类库,而一些第三方的类库由于缺少维护,从而导致类库中含有各种警告和各种内存泄漏,但并不影响运行. 倘若我们需要用到第三方库,而...
  • meegomeego
  • meegomeego
  • 2013年08月12日 15:09
  • 904

性能优化——内存泄漏(1)入门篇

一、简述本篇是作为内存泄漏入门,主要说的是一些关于内存泄漏的概念,包括什么是内存泄漏,内存分配的几种策略,为什么会造成内存泄漏 及 如何避免内存泄漏等。1、避免内存泄露的重要性对于一个APP的评测,最...
  • CSDN_LQR
  • CSDN_LQR
  • 2017年06月29日 15:05
  • 973

Cocos开发中性能优化工具介绍之Visual Studio内存泄漏检测工具——Visual Leak Detector

那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测功能,我们可以使用第三方工具Visual Leak Detector(以下简...
  • tonny_guan
  • tonny_guan
  • 2014年11月08日 21:51
  • 5465

Cocos开发中性能优化工具介绍之Visual Studio内存泄漏检测工具——Visual Leak Detector

那么在Windows下有什么好的内存泄漏检测工具呢?微软提供Visual Studio开发工具本身没有什么太好的内存泄漏检测功能,我们可以使用第三方工具Visual Leak Detector(以下简...
  • ctbinzi
  • ctbinzi
  • 2015年03月24日 17:52
  • 503

Android性能优化——常见的内存泄漏及处理方法

文章转载自:Sunzxyong 前言对于内存泄漏,我想大家在开发中肯定都遇到过,只不过内存泄漏对我们来说并不是可见的,因为它是在堆中活动,而要想检测程序中是否有内存泄漏的产生,通常我们可以借助Lea...
  • qq_22393017
  • qq_22393017
  • 2016年08月25日 11:29
  • 368
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:性能优化——内存泄漏(3)代码分析篇
举报原因:
原因补充:

(最多只允许输入30个字)