安卓内存泄露几种常见形式及解决方案
来源:互联网 发布:创新创业网络课答案 编辑:程序博客网 时间:2024/05/20 14:25
一.前言
1.内存溢出与内存泄露
内存溢出(oom),是指程序在申请内存时,没有足够的内存空间供其使用,出现oom;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 (memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
memory leak会最终会导致oom!
二.内存泄露的几种形式
1.匿名内部类的使用
a.Thread内存泄漏
这里最常见的形式就是使用new thread开启一个子线程.
子线程会对当前activity有一个隐式的强引用
当activity退出时候,如果子线程还在运行activity就不会释放.
running = true; new Thread(new Runnable() { @Override public void run() { while (running) { SystemClock.sleep(1000); runOnUiThread(new Runnable() { @Override public void run() { SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String date = sDateFormat.format(new Date()); tv.setText(date); } }); } } }).start();
LeakCanary检测结果
解决办法:
调用onDestroy后结束子线程
@Override protected void onDestroy() { super.onDestroy(); running = false; }
b.Timer内存泄露
这里既然thread使用有问题,那么我们用hander+Timer的形式可以吗,我们来看看.
结果抱歉,使用Timer和Thread无论从原理还是结果上都与handler一样.
public class HandlerAndTimerErr extends AppCompatActivity { Handler mhandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); tv.setText(msg.obj.toString()); } }; private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread_err); tv = (TextView) findViewById(R.id.tv); Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String date = sDateFormat.format(new Date()); Message message = new Message(); message.obj=date; mhandler.sendMessage(message); } }, 1, 1000); }}
LeakCanary检测结果
解决办法
@Override protected void onDestroy() { super.onDestroy(); timer.cancel(); }
2.纯handler的错误使用
如果仅使用handler还可以这样写
public class HandlerErr extends AppCompatActivity { Handler mhandler = new Handler(); private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread_err); tv = (TextView) findViewById(R.id.tv); mhandler.postDelayed(new Runnable() { @Override public void run() { SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String date = sDateFormat.format(new Date()); tv.setText(date); mhandler.postDelayed(this,1000); } },1000); }}
LeakCanary检测结果
解决办法
@Override protected void onDestroy() { super.onDestroy(); //移除当前handler发送的请求 mhandler.removeCallbacksAndMessages(null); }
3.context导致内存泄露
做一个单例的ToastUtils来显示toast是很多人会做的.你的ToastUtils是否这么写的?
public class ToastUtils { private static Toast toast; public static void ShowToast(Context context, String text){ if (toast==null) { toast = Toast.makeText(context, text, Toast.LENGTH_LONG); }else{ toast.setText(text); } toast.show(); }}
这样写其实有很大问题:
如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity退出时它的内存并不会被回收,这就造成泄漏了。
LeakCanary检测结果
解决办法:
1.使用ApplicationContext代替Activity的Context
2.如果必须要Activity的Context请换种写法吧,哈哈.
4.fragment中使用getActivity().findViewById()导致内存泄露
使用getActivity().findViewById()会导致fragment内存中不释放,虽然退出Activity可以让其释放,但是比如使用viewpager+fragment无限滑动就会导致内存过大
解决办法:
使用fragment的findviewbyId
5.leackCannary使用
leackCannary使用其实非常简单
githup链接: https://github.com/square/leakcanary
1.首先在你的build.gradle添加引用:
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1' }
2.在你的application中初始化
refWatcher = LeakCanary.install(this);
2.1监测fragment中的内存泄露
application中添加方法
public static RefWatcher getRefWatcher() { return refWatcher; }
baseFragment中调用
@Override public void onDestroy() { super.onDestroy(); //检测fragment中的内存泄露 RefWatcher refWatcher = SmartInfoApplication.getRefWatcher(); refWatcher.watch(this); }
3.提醒
这里其实就已经配置完成可以运行使用了.
注意,当你进入一个activity再退出之后等个三五秒如果有溢出就会提示的.
4.讲解
我们就拿这个例子来说
这个是告诉我们 ToastUtils下面有一个静态成员变量toast
它引用了一个context
这个context是ContextErr这个activity的一个实例(instance)
它导致了内存泄露.
5.源码地址
https://github.com/jin870132/memoryleakdemo
- 安卓内存泄露几种常见形式及解决方案
- 常见内存泄露及解决方案
- 常见内存泄露及解决方案
- 常见内存泄露及解决方案
- java几种常见内存泄露及处理方法
- java几种常见内存泄露及处理方法
- 常见内存泄露及解决方案(转)
- 常见内存泄露及解决方案(转)
- android常见的内存泄露及解决方案
- 安卓常见内存泄露解决办法
- 安卓内存泄露和解决方案
- 常见内存泄露及解决方案-选自ood启示录
- 常见内存泄露及解决方案(转自OOD启示录)
- 安卓内存泄露
- 安卓内存泄露
- 安卓内存泄露
- 什么是内存溢出与内存泄露,几种常见导致内存泄露的写法
- 什么是内存溢出与内存泄露,几种常见导致内存泄露的写法
- Unknown integral data type for ids : java.lang.String; nested exception is org.hibernate.id.Identifi
- 蓝桥杯 算法训练 表达式的计算(中缀转后缀表达式求值)
- D
- Centos7网络设置
- Version 1.7.0_121 of the JVM is not suitable for this product. Version: 1.8
- 安卓内存泄露几种常见形式及解决方案
- dfs序(HDU 5296,Annoying problem)
- 未来的程序员还会有今天的收入吗?
- 最长回文子串 C++
- Android视图SurfaceView
- HDU 1039 Easier Done Than Said?
- Hibernate的autocommit问题
- response.setContentType与 request.setCharacterEncoding 区别
- Android存储系统之架构篇