在Android
系统中,垃圾回收是自动的,比较隐蔽,这就导致一些内存问题表现的并不明显,出现问题后难以定位。常见的内存问题有内存泄漏、内存溢出(Out of Memory
)、内存抖动等。
我们做内存优化的主要原因有以下几点:
OOM
率,内存问题最常见的就是OOM
,申请的内存得不到释放就有可能造成OOM
;Android
系统中造成卡顿的原因有很多,其中就有内存引起的卡顿。内存问题之所以会影响到界面流畅度,是因为垃圾回收机制,在GC
的时候,所有的线程都要停止工作,包括主线程。因此,当GC
和绘制界面的操作同时触发时,绘制的任务就会被搁置,导致掉帧,也就是界面卡顿;Android
系统会按照特定的流程清理进程,优先考虑清理后台进程,如果某个应用在后台运行且占用的内存很多,就会被优先清理掉。如果我们希望APP
可以存活的久一点,不使用的内存应该尽快清理掉;至于哪些对象是需要回收的?使用跟搜索算法/可达性算法,选定一些对象作为GC Roots
,向下搜索,搜索所经过的路径称为引用链,当一个对象到GC Roots
没有应用链相连,则表明此对象需要回收。以下是可以作为GC Roots
的对象:
内存泄漏指的是我们的应用已经不需要某些对象了,但是,该对象的某些引用依旧没有释放,导致这些对象没有办法被回收。从内存上来看,就是某块内存已经不再被使用了,但是却没有办法被回收掉,从而造成了内存浪费。 比如说匿名内部类持有外部类的引用,导致外部类无法被回收,就会出现内存泄漏的问题。
内存泄漏会导致无法回收的内存越来越多,可用的内存越来越少,直到应用无更多可用的内存申请,严重的甚至出现OOM
。
OOM Out Of Memory
)内存泄漏一般导致应用卡顿,极端情况会导致OOM
,OOM
的原因是因为超过内存的阈值。原因主要有两方面:
OOM
;OOM
;能够消耗大量内存的,绝大多数是因为图片加载。这是OOM
出现最频繁的地方。图片加载,一个是控制每次加载的数量,二是保证每次滑动的时候不进行加载,滑动完进行加载。一般情况使用先进后出,而不是先进先出。 不过一般我们图片加载都是使用fresco
或者Glide
等开源库。
下面是导致内存溢出的两种情况:
通过命令行查看内存消耗情况(adb shell dumpsys meminfo 包名 -d
):
对于图片的内存优化:
200 x 200
,但是View
的宽高为100 x 100
,这个时候就要对图片进行缩放,通过inSampleSize
实现;API 29
中,将Bitmap
分为ALPHA_8
、RGB_565
、ARGB_4444
、RGBA_F16
、HARDWARE
六个等级:
ALPHA_8
:不存储颜色信息,每个像素占1
个字节;RGB_565
:仅存储RGB
通道,每个像素占2
个字节,对Bitmap
色彩没有高要求,可以使用该模式;ARGB_4444
:已弃用,用ARGB_8888
代替;ARGB_8888
:每个像素占用4
个字节,保持高质量的色彩保真度,默认使用该模式;RGBA_F16
:每个像素占用8
个字节,适合宽色域和HDR
;HARDWARE
:一种特殊的配置,减少了内存占用同时也加快了Bitmap
的绘制;100
像素的图片,ARGB_8888
占用了400
字节,RGB_565
才占用200
字节。所以在某些场景中,修改图片格式也可以达到减少一半内存的效果。Bitmap
所占用的内存比较大,如果频繁的创建和回收Bitmap
,很容易造成内存抖动,所以应该尽量复用Bitmap
内存。在 Android 3.0(API 级别 11)
开始,系统引入了 BitmapFactory.Options.inBitmap
字段。如果设置了此选项,那么采用 Options
对象的解码方法会在生成目标 Bitmap
时尝试复用 inBitmap
,这意味着 inBitmap
的内存得到了重复使用,从而提高了性能,同时移除了内存分配和取消分配。不过 inBitmap
的使用方式存在某些限制,在 Android 4.4(API 级别 19)
之前系统仅支持复用大小相同的位图,4.4 之后只要 inBitmap
的大小比目标 Bitmap
大即可BitmapRegionDecoder
来实现上面所说的这些关于Bitmap
的内存优化策略其实都比较简单,而且我们在开发中可能很少用到
因为我们常用的图片框架比如Glide
已经将这些都封装在里面了,所以一般情况下我们加载图片时不需要做这些特殊操作。
在短时间内频繁的创建或者销毁的对象,容易触发GC
,会引起内存抖动,比如在一个for
循环中创建临时对象实例。
因为GC
的时候所有的线程都会停止工作,因此导致卡顿。为了避免内存抖动,应该避免以下操作:
View.onDraw()
方法中创建对象,因为这个方法会被频繁调用;