1、定义
- 定义:内存不在使用,无法归还给应用程序的现象。
2、影响
- 影响:容易使应用程序发生内存溢出(Out Of Memory )。OOM(定义:应用程序所需内存 大于 安卓系统分配的内存);安卓系统为每个应用程序分配的内存有限,内存溢出容易导致应用程序崩溃。
3、原因
- 本质原因:持有引用者的生命周期 大于 被引用者的生命周期。导致本该回收的对象无法被回收。
4、Android 内存管理机制
- 定义:Android 内存管理:内存分配 和 内存回收。
- 管理的对象:进程、对象、变量。
- 管理的角色:Application Framework、Dalvik 虚拟机、Linux 内核。Application Framework 和 Linux 内核 负责 进程;Dalvik 虚拟机 负责 对象、变量 内存管理。
- 进程的内存分配与回收
1、分配:ActivityManagerService 管理进程的内存分配。
2、回收:1.Application Framework 按优先级决定回收类型(前台进程>可见进程>服务进程>后台进程>空进程)。2.Linux 内核回收具体进程。 - 对象、变量的内存分配与回收
内存分配策略 | 使用内存空间 | 存储数据 | 特点 |
---|---|---|---|
静态分配 | 方法区 | 类信息、常量、静态变量 | 1.编译时分配好。2.存在整个程序的运行区间。 |
栈分配 | 栈区 | 局部变量(基本数据类型、对象的引用) | 1.方法执行时,程序自动在栈中分配内存,方法结束,自动释放内存。2.效率高。3.内存容量有限。 |
堆分配 | 堆区 | 对象的实例、实例的成员变量 | 1.创建对象,程序在堆中分配内存。2.Java 垃圾回收器(垃圾回收算法),回收内存 |
算法名称 | 算法思想 | 优点 | 缺点 | 应用场景 |
---|---|---|---|---|
标记-清除 算法 | 1.标记阶段:标记出所以要回收的对象。2.清除阶段:清除所以被标记的对象。 | 实现简单 | 1.效率问题:标记 和 清除 两个过程,效率低。2.空间问题:产生大量不连续的内存碎片。 | 对象存活率高;垃圾回收频率低。(如:老年代) |
复制 算法 | 1.将内存分为大小相等的两块,每次使用其中的一块。2.当使用的这块内存用完,将还存活的对象复制到另一块内存。3.最后将使用的那块内存一次性清理掉。 | 1.解决标记-清除效率问题:每次回收内存一半区域。2.解决标记-清除空间问题:将存活对象按顺序复制到另一块内存。 | 1.每次使用的内存缩小为原来一半。2.对象存活率高的情况要做较多复制操作,效率低。 | 对象存活率低;垃圾回收频率高。(如:新生代) |
标记-整理 算法 | 1.标记阶段:标记出所以要回收的对象。2.整理阶段:让所有存活的对象都向一端移动。3.清除阶段:清除所以被标记的对象。 | 1.解决标记-清除效率问题:一次清除端外区域。2.解决标记-清除空间问题:存活的对象移到一端。 | 步骤繁琐:标记、整理、清除。 | 对象存活率高;垃圾回收频率低。(如:老年代) |
分代回收 算法 | 根据对象的存活周期将堆内存分为:新生代和老年代。1.新生代:对象存活率低;垃圾回收频率高。采用 复制算法。2.老年代:对象存活率高;垃圾回收频率低。采用 标记-清除、标记-整理 算法。 | 效率高,空间利用率高:根据不同区域特点选择不同垃圾回收算法 | 虚拟机采用此算法 |
如何判断一个对象是否可以被回收:
1、引用计数算法:引用计数为 0 的对象实例可以被当作垃圾回收。(很难解决对象相互引用的问题)。
2、可达性分析算法:不可到达的对象表示可以回收。
5、常见内存泄漏原因及解决方案
1、集合类
2、static 关键字修饰成员变量
3、非静态内部类 / 匿名类
4、资源对象使用后未关闭
5、其它(Context、WebView、Adapter)
- 集合类
1、原因:集合添加元素,引用了该元素对象,导致对象不可以被回收,导致内存泄漏。
2、解决方案:集合使用后清除对象。(如:list.clear();list = null ;) - static 关键字修饰成员变量
1、原因:static 修饰的生命周期 = 应用程序的生命周期。(如:static Context = mContext;)
2、解决方案:1、使用 Application 的 Context。2、使用弱引用(WeakReference)。 - 非静态内部类 / 匿名类
1、原因:非静态内部类 / 匿名类 默认持有外部类的引用。
2、解决方案:1、使用 Application 的 Context。2、使用弱引用(WeakReference)。3、使用静态内部类。(如:多线程 Thread、Handler 等)。 - 资源对象使用后未关闭
1、原因:资源对象使用后未关闭,资源将不会被回收,导致内存泄漏。
2、解决方案: 资源对象使用后关闭。(如:广播 BroadcastReceiver-unregisterReceiver()、文件流-InputStream.close()、数据库游标 Cursor-Cursor.close、图片资源 Bitmap-Bitmap.recycle();Bitmap = null;、动画停止)。 - 其它(Context、WebView、Adapter)
1、Context:使用 Application 的 Context;使用弱引用。
2、WebView:动态创建 WebView;不使用时销毁。
3、Adapter:如 ListView:使用缓存 convertView;使用 ViewHolder。或 RecycleView 替代。
5、辅助分析内存泄漏
- Android Profiler:Androidstudio 3.0自带工具,分析 CPU、MEMORY、NETWORK 等。
- LeakCanary:Square 出品的检测内存泄漏的开源库。