为什么进行内存优化?
1. app运行内存限制,OOM导致app奔溃
2. app性能:流畅性、响应速度和用户体验
获取Android系统默认给每个app分配的内存上限:
ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);int memoryClass = activityManager.getMemoryClass(); //单位为m 经测试本人红米3S为192M
如果超出这个限制,将会导致应用OOM。
Android的内存管理方式:
Android系统的内存分配和回收方式:
1. Android底层是Linux系统,一个 App 通常就是一个进程对应一个虚拟机,一个虚拟机在linux系 统中有一个进程与之对应
2. GC(垃圾回收器)只在Heap(堆)剩余空间不够时才会发出垃圾回收
3. GC触发时,所有的线程都会被暂停
App 内存限制机制:
1. 每个 App 分配的最大内存限制,随不同设备而不同
2. 吃内存大户:图片
3. 为什么要限制:多任务系统要使每个 App 具有足够的内存空间保证都能够正常运行
切换应用时后台 App 清理机制:
1. 后台各种 App 切换时的LRU Cache(最近使用的排在最前面,最少使用的可能被清理掉)
2. 系统内存不够清理后台 App 时候回调onTrimMemory(int level)方法
onTrimMemory(int level):
4.0 之后提供的一个API,这个 API 是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,从而避免被 Android 系统杀死,提高应用程序的用户体验.当内存变化时Android系统会根据不同等级的内存使用情况,调用这个函数,并传入对应的等级;我们可以根据该等级的类型,如果等级属于危险级别的就可以做相应的处理,把我们 App 里面不同的内存尽快清理掉,这样我们的 App 内存占用就相对小一点,从而降低被清理掉的可能性。
监控内存的几种方法:
1. 代码:以下两个相加就是系统当前分配给这个 App 的内存大小
已经分配的内存大小:
float totalMemory = Runtime.getRuntime().totalMemory() * 1.0f / (1024 * 1024);
空闲的内存:
float freeMemory = Runtime.getRuntime().freeMemory() * 1.0f / (1024 * 1024);
2. AS的Android Monitor监控:
3. AS的Android Device Monitor
依次点击:Tools-->Android-->Android Device Monitor,选中要查看的进程,点击左上角绿色水杯Update Heap-->右边Cause GC;
Heap Size:堆的大小 = Allocated:分配的大小 + Free:空闲的大小;
data Object和class Object如果不稳定、不断变大就考虑是否内存泄漏
Android内存优化方法
数据结构优化:
1. 频繁字符串拼接用StringBuilder
字符串通过+的方式进行拼接,会产生中间字符串内存块,会被GC回收掉,同时也是效率低的,耗时较长
2. ArrayMap、SparseAray代替HashMap
3. 内存抖动:变量使用不当引起
比如突然申请很多变量或内存空间,但是这个内存空间的数据很快就用完了,过了一会又申请很多,这时如果heap的内存不够,GC就会回收之前的内存,这个过程会暂停所有线程,如果这个过程在较短的时间内重复出现可以从Monitor中看到锯齿一样的抖动,如果内存抖动耗时比较长,就会影响 App 运行的流畅性,用户体验就会很差
4. 再小的Class也至少耗费0.5KB
5. HashMap一个entry需要额外占用32B
6. 避免在android里面使用Enum
7. 减少bitmap的内存占用,图片质量不高时可以考虑使用RBG_565
8. 万恶的static
static声明变量的生命周期和 App 的生命周期一样的,大量的使用,会占据内存空间不释放,积少成多会造成内存的不断开销,直至挂掉;
一般用来修饰基本数据类型或者轻量级对象,尽量避免修复集合或者大对象,常用作修饰全局配置项、工具类方法、内部类。
对象复用:
1. 复用系统自带的资源(布局、资源文件等)
2. listView、GridView的convertView复用(现在都在用RecyclerView了吧哈~)
3. 避免在onDraw()方法里面执行对象的创建
避免内存泄漏:
1. 内存泄漏会导致剩余可用 heap 内存越来越少,频繁触发GC直至OOM
2. 尤其activity泄漏
被静态集合引用;
内部类中开启线程会保持外部activity的引用,如果执行耗时操作,当activity销毁时还未执行完毕导致activity无法被回收,导致内存泄漏。
3. 尽量用Application Context而不是Activity Context
4. 注意Cursor对象是否及时关闭
5. 谨慎handler
Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有的消息的处理,包括待处理的消息;
声明handler的内部类为static。
6. 页面背景和图片加载
设置背景和图片的时候,如果是纯色,尽量使用color
规则图形,尽量使用shape画图
稍微复杂点,可以使用9patch图
不能使用9patch的情况下,针对几种主流分辨率的机型进行切图
内存泄漏:逻辑上创建的对象(内存块)使用完后不用了,应该被GC回收变成Free内存,但由于代码瑕疵,导致这块内存虽然不用了,但仍然被其他地方引用着,使得GC无法对其进行回收,大量的内存泄露会导致OOM。
OOM 问题优化:
OOM问题分析:
1. OOM的必然性和可解决性
程序开发的性能不好,超出最大内存限制就必然发生,手机厂家在设定内存上限的时候肯定已经考虑过了,你的 App 如果优化的比较好,肯定可以在手机上顺畅的运行,不会出现OOM问题,出现该问题肯定是优化有问题。
2. OOM绝大部分发生在图片
强引用、软引用:
变量的生命周期:跟其所处的环境有关,一般变量在activity的成员位置,那么他的生命周期和activity是一样的,如果在方法里面,它的生命周期跟方法有关,方法退出了,其生命周期就结束了;当变量生命周期终止的时候,GC是会对它进行回收的。
被软引用包裹的变量:softReference<类型>,假设其在activity成员位置,如果在activity的活动的生命周期中,内存不够了,对于软引用所占有的内存空间GC是可以对其进行回收的,所以软引用和强引用的关系是:
强引用在生命周期里它的内存空间是不会被回收的,而软引用在它的生命周期里,只要是内存不够了,那么GC是可以对其进行回收的,所以在运行的过程中有可能会出现这个引用变成空的了它不再指向任何一个内存空间。
优化OOM问题的方法
1. 注意临时Bitmap对象的及时回收(置空或recycler)GC会很快处理掉
2. 避免Bitmap浪费
3. Try catch 某些大内存分配的操作
4. 加载Bitmap:缩放比例(采样率)、解码格式(RGB_565))、局部加载(解决大部分图片相关OOM问题)
5. 最后推荐一个检测 App 内存泄露的工具
LeakCanary(具体使用网上很容易查到,很简单就可以集成,就不赘述了)