Android内存泄露与内存溢出
来源:互联网 发布:数据安全 国家安全法 编辑:程序博客网 时间:2024/05/22 17:01
一、 内存泄漏与内存溢出(OOM)
1. 内存泄露
- 垃圾回收器无法回收原本应该被回收的对象,这个对象就引发了内存泄露。
- 内存泄露的危害: (1)过多的内存泄露最终会导致内存溢出(OOM)(2)内存泄露导致可用内存不足,会触发频繁GC,不管是Android2.2以前的单线程GC还是现在的CMS和G1,都有一部分的操作会导致用户线程停止(就是所谓的Stop the world),从而导致UI卡顿。
2. 内存溢出(OOM)
- Android为每个进程设置Dalvik Heap Size阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。如果APP想要分配的内存超过这个阈值,就会发生OOM。
- ActivityManager.getMemoryClass()可以查询当前APP的Heap Size阈值,单位是MB。
- 在3.x以前,Bitmap分配在Native heap中,而在4.x之后,Bitmap分配在Dalvik或ART的Java heap中。
- Android 2.x系统,当dalvik allocated + native allocated + 新分配的大小 >= dalvik heap 最大值时候就会发生OOM,也就是说在2.x系统中,考虑native heap对每个进程的内存限制。
- Android 4.x系统,废除了native的计数器,类似bitmap的分配改到dalvik的java heap中申请,只要allocated + 新分配的内存 >= dalvik heap 最大值的时候就会发生OOM(art运行环境的统计规则还是和dalvik保持一致),也就是说在4.x系统中,不考虑native heap对每个进程的内存限制,native heap只会收到本机总内存(包括RAM以及SWAP区或分页文件)的限制。
二、 LeakCanary内存检测工具配置
LeakCanary在GitHub上地址:https://github.com/square/leakcanary
在app的build.gradle中加入:
dependencies { //leakcanary内存检测三方库 debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'}
在项目的Application中加入(没有则创建):
@Override public void onCreate() { super.onCreate(); //初始化内存检测工具 if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... }}
三、 内存泄漏检测案例
非静态内部类引起的内存泄漏
导致内存泄漏的错误代码:
public class LeakActivity extends AppCompatActivity { private static User mUser; …… /** * 非静态内部类引起的内存泄漏 */ private void innerClassLeak() { mUser = new User(); } class User { private String userName; }}
内存泄漏检测工具界面截图:

分析结果:
从下往可以看出导致内存泄漏是LeakActivity中private static User mUser;导致。导致该Activity销毁时释放不了.
解决后代码:
private static User mUser; …… /** * 非静态内部类引起的内存泄漏 */ private void innerClassLeak() { mUser = new User(); } static class User { private String userName; }}
将非静态类加入static修饰变为静态类,或者去除 private static User mUser;中static修饰符
Thread引起的内存泄漏
导致内存泄漏的错误代码:
/** * 线程引起内存泄漏 */private void threadLeak() { new Thread(new Runnable() { @Override public void run() { //耗时操作 SystemClock.sleep(10000);//睡眠10s,模拟耗时操作 } }).start();}
内存泄漏检测工具界面截图:

分析结果:
可以看出内存泄漏的原因是LeakActivity中有个任务导致,当次Activity销毁时,此线程任务未完成,导致此Activity无法释放。
解决后代码:
/** * 线程引起内存泄漏 */private void threadLeak() { new MyThread().start();}static class MyThread extends Thread{ @Override public void run() { //耗时操作 SystemClock.sleep(10000);//睡眠10s,模拟耗时操作 super.run(); }}
自定义线程池工具类来开启此线程也可以。
Handler引起的内存泄漏
导致内存泄漏的错误代码:
Handler handler= new Handler();/** * Handler导致内存泄漏 */private void HandlerLeak() { handler.postDelayed(new Runnable() { @Override public void run() { handler.postDelayed(this,1000);//每隔1000毫秒,循环发送消息 } },1000);}
内存泄漏检测工具界面截图:

分析结果:
从下到上可以看出LeakActivity中消息任务导致内存泄漏,此Activity关闭时消息任务还未关闭,导致此Activity未能销毁。
解决后代码:
Handler handler= new Handler();/** * Handler导致内存泄漏 */private void HandlerLeak() { handler.postDelayed(new Runnable() { @Override public void run() { handler.postDelayed(this,1000);//每隔1000毫秒,循环发送消息 } },1000);}@Overrideprotected void onDestroy() { super.onDestroy(); handler.removeCallbacksAndMessages(null);//remove正在执行的消息任务}
当Activity销毁时调用关闭所有消息任务
单例Toast引起内存溢出
导致内存溢出的错误代码:
/** * 静态Toast引起的内存泄漏 */private void ToastLeak() { ToastUtils.showToast(this,new Date().getTime()+"");}private static Toast toast;public static void showToast(Context context, String msg){ if(toast==null) toast = Toast.makeText(context,"",Toast.LENGTH_SHORT); toast.setText(msg); toast.show();}
内存泄漏检测工具界面截图:

分析结果:
从下至上看出LeakActivity 中Toast(这里使用了静态Toast) 的Context导致LeakActivity无法销毁,内存泄漏
解决后代码:
/** * 静态Toast引起的内存泄漏 */private void ToastLeak() { ToastUtils.showToast(this,new Date().getTime()+"");}private static Toast toast;public static void showToast(Context context, String msg){ if(toast==null) toast = Toast.makeText(context.getApplicationContext(),"",Toast.LENGTH_SHORT); toast.setText(msg); toast.show();}
这里使用的是应用全局的Context。
什么是Context:https://possiblemobile.com/2013/06/context/
四、如何避免内存泄漏
- 使用轻量的数据结构:使用ArrayMap/SparseArray来代替HashMap,ArrayMap/SparseArray是专门为移动设备设计的高效的数据结构。
- 不要轻易使用Enum。
- Bitmap压缩处理:使用知名的第三方开源库处理。
- 不要使用String进行字符串拼接:频繁的字符串拼接,使用StringBuffer或者StringBuilder代替String,可以在一定程度上避免OOM和内存抖动。
- 谨慎使用static对象:static对象的生命周期过长,应该谨慎使用。
- Context持有导致内存泄漏:Activity中的context被传递到其他实例中,这可能导致自身被引用而发生泄漏。
- 【Android高级】内存泄露与内存溢出
- Android 内存溢出与内存泄露
- Android内存泄露与内存溢出
- android:内存泄露与内存溢出区别、内存泄露定位
- 内存泄露与溢出
- 内存泄露与内存溢出
- 内存溢出与内存泄露
- 内存泄露与内存溢出
- 内存泄露与内存溢出
- 内存溢出与内存泄露
- 内存泄露与内存溢出
- 内存泄露与内存溢出
- 内存泄露与溢出区别
- 内存泄露与溢出区别
- Linux内存泄露与溢出
- Android内存泄露与内存溢出是什么,什么情况是内存泄露,什么情况是内存溢出
- JAVA内存泄露与内存溢出
- 内存溢出与内存泄露的区别
- 树的层数
- Nginx安装以及使用,加载Https模块,以及gzip模块
- ubuntu下运行,配置java环境
- java中重载和重写的区别
- java 集合框架的一个澄清
- Android内存泄露与内存溢出
- 运算符重载的特征标顺序
- Node.js的读取文件的异步过程基础的讲解
- COLLATION 'utf8_bin' is not valid for CHARACTER SET 'utf8mb4'
- 查找
- leetcode--Move Zeroes
- Qt 贪吃蛇小游戏
- [LeetCode]24. Swap Nodes in Pairs
- java入门实例 奥特曼打小怪兽