内存对于Android应用来说是一种稀缺资源,系统分配给每一进程的内存大小有限,虽然Java本身就自带内存回收机制,但是在写代码的过程中如果没有内存回收的意识,还是很容易出现内存泄漏,严重可能可能会导致内存抖动,甚至发生OOM内存溢出的Crash。
【内存抖动是由于短时间产生大量的零时对象,此时维持程序运行的可支配内存不足,导致频繁的GC,在GC的时候除了GC线程所有线程都会挂起,这会表现出应用卡顿,反复重复上述过程就是内存抖动】
所以内存优化是必要性的!
内存问题:
内存抖动:短时间内产生大量零时对象,触发频繁GC。
内存泄漏:本应该被回收的对象,却被无意义的持有了引用导致无法GC回收释放。
内存溢出:要求从虚拟机申请一块内存,但是可支配内存不足时就会发生内存溢出。内存溢出很多时候都是由于内存泄漏堆积导致的,还有申请内存使用不当。
要不要写详细点?
有必要了解的知识,Java虚拟机内存模型,在java虚拟机中内存分为5块区域:
虚拟机栈:Java方法调用执行的管理区域(一个栈帧对应一个方法,栈帧的入栈出栈对应着一个方法的调用)。
本地方法栈:Native(C、C++)方法对应的栈帧管理,同java。
程序计数器:对应着程序字节码的执行行数计数,在Thread切换的时候负责记录程序执行的位置。
【以上三块内存区域是线程私有的,下边两块是线程公用的内存区域,GC也是发生在这两块】
方法区:存放对象引用、静态成员、常量。
堆:对象存放的区域,也是GC的主要发生地,也是五块内存区域中最大的一块内存区域。
当发生GC的时候,会去回收堆中可回收的对象。堆分为两块区域,新生代和老年代,分别执行者不同的内存管理算法。
新生代(存放朝生暮死的对象):(hotspot虚拟机在新生代内存分为8:1:1)
- 标记-整理算法
将还存活的对象全部压缩到一块区域,剩下区域的都是可回收的对象,将被回收。 - 标记-清除算法
在标记-整理算法中,还存活的对象都将年龄标记计算加一。 - 复制整理算法
在新生代中标记的年龄达到阈值的时候,将被复制到老年代,说明这中对象是kennel长时间存在的。
老年代(存放生命长久的对象): - 标记整理算法
没有被引用的就回收掉,还存活的就压缩到内存的一块区域,保证内存的连续性,缓解碎片化问题。 - 标记-清除算法
没有被引用的就回收掉。
哪些对象是被判定可回收对象?
从GC Roots对象往下追溯,没有引用链可达的对象都是可被回收的对象。
可以作为GC Roots根节点的对象:常量池中常量引用的对象、栈中本地方法表成员引用的对象、静态成员引用的对象。
Java虚拟机内存这块额外多了解了解。。。
内存分析工具
-
Android Studio自带的Memory Profile
Memory Profile能够很直观的帮助我们分析代码运行中内存的使用情况。
profile
app heap:这一列展示了内存中所存在的这些类
Allocations:是分配了多少对象
Native Size:主要反映Bitmap所使用的像素内存
Shallow Size:该实例的大小
Retained Size:该实例所支配的内存大小
- LeakCanary开源库
LeakCanary可以很方便的帮助我们在App开发测试阶段发现内存了泄漏。
在build.gradle添加依赖
//LeakCanary
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
在Application的onCreate方法中初始化
if (!LeakCanary.isInAnalyzerProcess(this)) {
LeakCanary.install(this);
}
然后当你的App中出现内存泄露时,手机桌面会出现一个Leaks的图标,点击进去可以看到产生泄露的记录列表,点击列表条目可以展开具体的泄露信息:
图片来自一叶难障目
LeakCanary原理
弱引用被回收时,引用会被添加到引用队列。
给被监控的对象分为一个UUID,并存储到set集合中。使用弱引用对被监控的对象进行包装,然后主动调用GC,在GC执行完毕后遍历引用队列对应移除set集合中的UUID,没有被移除的UUID对应的对象就被判定是存在内存泄漏的对象。
内存优化的策略
- 善用线程池。
- 善用对象池复用对象。
- Bitmap图片资源压缩,用完及时回收释放内存。
- 注册器、数据库游标记得及时回收。
- LargeHeap属性申请更多的内存(虽然有点耍流氓,但还是应该向系统申请)。
- onTrimMemory、onLowMemory(系统给的低内存的回调,可以根据不同的回调等级去处理一些逻辑)。
- 使用一些内存优化后的特定数据结构,例如:SparseArray、ArrayMap。
网友评论