一:安卓内存管理机制
内存优化:避免因不正确使用内存,缺乏管理,从而出现内存泄漏、内存溢出、内存空间占用过大等问题,最终导致应用程序奔溃。
进程回收策略:Application Framework决定回收的进程类型:android中的进程是托管的;当进程空间紧张时,会按照进程优选级低-->高的顺序自动回收进程分配
对象内存策略:
堆:JVM管理的内存中最大的一块,所有线程共享;用来存放对象实例,几乎所有的对象实例都在堆上分配内存;此区域也是垃圾回收器(Garbage Collection)主要的作用区域,内存泄漏就发生在这个区域。
方法区:方法区存放的是类信息、常量、静态变量,所有线程共享区域。
虚拟机栈:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息,线程私有区域。
本地方法栈:与虚拟机栈类似,区别是虚拟机栈为虚拟机执行java方法服务,本地方法为虚拟机使用到的Native方法服务。
程序计数器:可看做是当前线程服务所执行的字节码的行号指示器,如果线程在执行java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址;如果执行的是Native方法,这个计数器的值为空(Undefined)。
二:内存释放
2.1标记回收算法
- 标记所有需要回收的对象
- 统一回收所有被标记的对象
- 它的优点:实现比较简单。缺点也很明显:标记、清楚效率不高,产生大量内存碎片
2.2复制算法
- 将内存划分为大小相等的两块
- 一块内存用完之后复制存活对象到另一块
- 清理另一块内存
- 它的优点:实现简单,运行高效,每次仅需遍历标记一半的内存区域。而缺点则会浪费一半空间,代价大。
2.3标记整理法
- 标记过程与“标记回收算法”一样
- 存活对象往一端进行移动
- 清理其余内存
- 优点:避免标记回收导致的内存碎片,避免复制算法的空间浪费
2.4分代收集算法
- 结合多种算法优势
- 新生代对象存活率低,复制
- 老年代对象存活率高,标记整理
三:常见内存问题和优化方案
3.1内存泄漏
指程序在申请内存后,当该内存不需再使用,但却无法被释放。
常见内存泄漏原因:
- 集合类:集合添加元素后,仍引用着集合元素对象,导致该集合元素对象不可被回收,从而导致内存泄漏。
- Static关键字修饰的成员变量:若使被static关键字修饰的成员变量引用耗费资源过多的实例(如Context),则容易出现该成员变量的生命周期 > 引用实例生命周期的情况,当引用实例需要结束生命周期销毁时,会因静态变量的持有而无法被回收,从而出现内存泄漏。
- 非静态内部类/匿名类:非静态内部类所创建的实例 = 静态(其生命周期 = 应用的生命周期),会因非静态内部类默认持有外部类的引用而导致外部类无法释放资源对象使用后未关闭。
3.2内存抖动
内存大小不断浮动
- 频繁创建大量临时小对象,导致内存碎片
- GC频繁回收内存会导致卡顿。当需要分配内存时,虽然总体上会有剩余内存可分配,但由于内存不连续,导致系统无法整块分配,导致OOM
3.3代码质量
- 代码量:减少不必要的类、对象、功能库
- 使用安卓优化后的数据结构,包括SparseArray、SparseBooleanArray和LongSparseArray。
- 使用占用内存小的数据类型,避免使用枚举类型。
- 根据不同的应用场景,选择不同的引用类型:
软引用:一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
弱引用:如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
四:分析工具
- 使用adb shell dumpsys meminfo packageName查看应用占用内存大小、Object占用大小情况
- showmap可以按照object列出内存的占用情况,adb shell showmap process id(实践使用该命令,无效??)
- Memory Profiler是android Profiler中的一个组件,可帮助你识别导致应用卡顿、冻结甚至奔溃的内存泄漏和流失。它显示一个应用内存使用量的实时图表,让你可以捕获堆转储、强制执行垃圾回收以及跟踪内存分配。(堆转储,使用MAT工具分析)
- 上述生成的hprof文件,使用MAT查看(后期要加以实践)
内存优化措施:
- 使用统一的线程池
- 通道连接使用一个线程对象常驻
- 对于初始化赋值为系统分配默认零值的静态变量和成员变量,去掉初始化赋值,系统会有个默认零值,不要重复赋值,去掉,可以节省代码字节码。
- 不适用hashMap,使用SparseArray、ArrayMap代替,ArrayMap比hashMap内存占用更少
- 数据存储:配置文件中存储XML的key和value是否必要,可以通过ArrayMap保存。
- 优化日志打印:避免拼接过多的字符串