定义
内存泄漏(Memory Leak):指 程序在申请内存后,当该内存不需再使用但却无法被释放的现象。
内存溢出(OOM):应用程序所需的内存超出了为其分配的内存限额。
Android将进程分为5个优先等级:
前台进程
可见进程
服务进程
后台进程
空进程
避免内存溢出(OOM)
释放强引用,使用软引用和弱引用;
图片加载
在内存中压缩图片
使用完图片后及时回收图片所占内存
降低要显示的图片色彩质量
查询图片信息时不把图片加载到内存中
常见的内存泄露原因 & 解决方案
1. 集合类内存泄露
说明:集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄露。
例子
static List mList = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Object obj = new Object();
mList.add(obj);
obj = null;
}
复制代码当mList没用的时候,我们如果不做处理的话,这就是典型的占着茅坑不拉屎,mList内部持有者众多集合元素的对象,不泄露天理难容啊。
解决方案
解决这个问题也超级简单。把mList清理掉,然后把它的引用也给释放掉。
mList.clear();
mList = null;
2、静态变量内存泄漏
说明:静态变量的生命周期跟整个程序的生命周期一致。只要静态变量没有被销毁也没有置null,其对象就一直被保持引用,也就不会被垃圾回收,从而出现内存泄露。
例子
public class MainActivity extends Activity {
public static Test sTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sTest = new Test(this);
}
}
//外部Test类
public class Test {
Test(Context context) {
}
}
sTest作为静态变量,并且持有Activity的引用,sTest的生命周期肯定比Activity长。因此当Activity退出后,由于Activity仍然被sTest引用到,所以Activity就不能被回收,造成了内存泄露。
Activity这种占用内存非常多的对象,内存泄露的话影响非常大。
解决方案
静态变量
在不用静态变量时置为空,如:
sTest = null;
Context
如果用到 Context,尽量去使用 Applicaiton 的 Context,避免直接传递 Activity,比如:
sTest = new Test(getApplicationContext());
Activity
若一定要使用Activity,建议使用弱引用或者软引入来代替强引用。如下:
//弱引用
WeakReference weakReference = new WeakReference(this);
Activity activity = weakReference.get();
//软引用
SoftReference softReference=new SoftReference(this);
Activity activity1 = softReference.get();
3. 非静态内部类(匿名类)内存泄露
说明:非静态内部类 (匿名类)默认就持有外部类的引用,当非静态内部类(匿名类)对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。
1. Handler 内存泄露
如果 Handler 中有延迟的任务或者是等待执行的任务队列过长,都有可能因为 Handler 继续执行而导致 Activity 发生泄漏。
1.首先,非静态的Handler类会默认持有外部类的引用,包含Activity等。
2.然后,还未处理完的消息(Message)中会持有Handler的引用。
3.还未处理完的消息会处于消息队列中,即消息队列MessageQueue会持有Message的引用。
4.消息队列MessageQueue位于Looper中,Looper的生命周期跟应用一致。
因此,此时的引用关系链是 Looper -> MessageQueue -> Message -> Handler -> Activity。所以,这时退出Activity的话,由于存在上述的引用关系,垃圾回收器将无法回收Activity,从而造成内存泄漏。
解决方案
静态内部类+弱引用
private static class MyHalder extends Handler {
private WeakReference mWeakReference;
public MyHalder(Activity activity) {
mWeakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//...
}
}
Activity退出时,移除所有信息(不建议使用)
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
2. 多线程引起的内存泄露
我们一般使用匿名类等来启动一个线程,如下:
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
同样,匿名Thread类里持有了外部类的引用。当Activity退出时,Thread有可能还在后台执行,这时就会发生了内存泄露。
解决办法:
静态内部类
private static class MyThread extends Thread{
//...
}
Activity 退出时,结束线程
4. 未关闭资源对象内存泄露
说明:一些资源对象需要在不再使用的时候主动去关闭或者注销掉,否则的话,他们不会被垃圾回收,从而造成内存泄露。
常见的需要主动关闭的资源对象:
注销广播
如果广播在Activity销毁后不取消注册,那么这个广播会一直存在系统中,由于广播持有了Activity的引用,因此会导致内存泄露。
unregisterReceiver(receiver);
关闭输入输出流等
在使用IO、File流等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果不及时关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就应该及时关闭,以便缓冲能得到释放,从而避免内存泄露。
InputStream.close();
OutputStream.close();
回收Bitmap
Bitmap对象比较占内存,当它不再被使用的时候,最好调用Bitmap.recycle()方法主动进行回收。
Bitmap.recycle();
Bitmap = null;
停止动画
属性动画中有一类无限动画,如果Activity退出时不停止动画的话,动画会一直执行下去。因为动画会持有View的引用,View又持有Activity,最终Activity就不能给回收掉。只要我们在Activity退出把动画停掉即可。
animation.cancel();
销毁WebView
WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的destory()方法来销毁它以释放内存。此外,WebView在Android 5.1上也会出现其他的内存泄露。具体可以看下这篇文章:WebView内存泄漏解决方法。
所以,要防止WebView内存泄露还是比较复杂的。代码如下:
@Override
protected void onDestroy() {
if( mWebView!=null) {
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}
mWebView.stopLoading();
// 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
mWebView.getSettings().setJavaScriptEnabled(false);
mWebView.clearHistory();
mWebView.clearView();
mWebView.removeAllViews();
mWebView.destroy();
}
super.on Destroy();
}
检测工具
1. MAT(Memory Analysis Tools)
简介
一个Eclipse的 Java Heap 内存分析工具 ->>下载地址
作用
查看当前内存占用情况
2. Memory Profiler
简介
Memory Profiler 是 Android Profiler 中的一个组件,可以帮助你分析应用卡顿,崩溃和内存泄露等等问题。
作用
跟踪系统 / 应用的内存使用情况。核心功能如下
3. Leakcanary
简介
Leakcanary 是 Square 开源的一个库,能够自动检测发现内存泄露。
作用
检测内存泄漏