Android 内存问题与优化避免

比起前几年,现在的 Android 设备拥有更大的内存。但是,即使现在可以使用更多的内存,也是有一个上限的,具体大小和各个厂商的设置有关。如果内存使用不当,还是会影响到APP的性能。内存问题主要有两类,一是内存溢出,二是内存泄漏。解决内存问题,主要靠借助工具检测分析,然后做代码优化。

一、Android 应用开发中的内存问题

1.1 单个进程可用内存限制

目前的 Android 设备,动辄4G、6G甚至8G内存,但是,Android设备的内存再大,每个应用可以使用的内存大小也是有限制的,具体可以使用的最大内存和各大厂商的出厂设置有关。
可以通过adb 查看应用可以使用的最大内存:

adb shell cat /system/build.prop
// ...
dalvik.vm.heapstartsize=8m
dalvik.vm.heapgrowthlimit=256m
dalvik.vm.heapsize=512m
// ...

上面的设备是 galaxy s7,可以使用的最大内存为512m,这需要在 manifest.xml 中 application 节点添加 android:largeHeap=”true”,否则,最多只能使用256m。
也可以直接查看它们的值:

adb shell getprop dalvik.vm.heapgrowthlimit
256m
adb shell getprop dalvik.vm.heapsize
512m
adb shell getprop dalvik.vm.heapstartsize
8m

现在的应用,对于图片质量的要求越来越高,动辄几百K,甚至上M一张图片,对于需要加载大量图片的应用来说,256m内存,分分钟就上去了。256m说大不大,说小不小,但是使我们的应用尽量少用内存总不会错。因为占用内存越大,gc频率就可能越高,然后就会出现卡顿的现象。

1.2 内存溢出

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出,简单来说就是,需要的内存超过了可以使用的最大内存值。一旦出现内存溢出,就会报 out of memory 的错误,app crash。 导致内存溢出的原因有很多,这里根据项目中曾经出现过的做一个总结:

  1. 直接加载原图
    需要浏览大图的时候,使用 ImageView 直接加载原图;或者需要加载Bitmap时直接读取了原图大小。
  2. 使用图片背景
    app的每个页面都使用一张图片作为背景(表示对图片背景深恶痛绝),导致内存暴增,然后OOM。
  3. 接口处理太多数据
    有接口需要将文件如图片等进行URLEncode为字符串,然后通过接口上传到服务器,然后一次处理多个文件,导致OOM。
  4. 内存泄露并堆积
    Activity和对话框等内存泄露,得不到释放,导致OOM。

1.3 内存泄漏

内存泄漏是指程序在申请内存后,无法释放已申请的内存空间。内存泄露和内存溢出是有区别的,内存溢出必然导致OOM,而内存泄露则可能导致内存溢出。内存泄露的原因有很多,下面是比较常见的几个原因:

  1. 静态变量导致内存泄露
  2. 非静态内部类导致内存泄漏,如Handler等,持有Activity的引用无法释放
    Activity中非静态内部类如Handler等,并不是总会引起内存泄漏,只是可能。
  3. 线程未完成任务导致Activity不能释放,内存泄漏,如Thread、AsyncTask、Timer和TimerTask等。
  4. WebView导致内存泄漏
  5. 错误使用单例模式(持有Activity的Context)
  6. 资源未关闭造成的内存泄漏(IO操作,数据库操作)
  7. 属性动画造成内存泄露
  8. 未取消注册或回调导致内存泄露

二、内存优化方法

内存优化可以从四个方向进行:开源,节流,复用,回收。开源,即必要的时候使用largeHeap、多进程等,使应用可以使用更多的内存,需要慎重考虑,因为largeHeap和多进程引进的问题比它们带来的优化更多。节流,减少不必要的内存分配,这个需要在有一个良好的编码习惯,是每个开发者应该尽力做到的。复用,顾名思义,就是可以复用的对象就不要重复创建,如ListView 使用ViewHolder进行优化就是复用了View。

2.1 开源

让应用使用最大内存

让应用可以使用最大内存,只需要在 manifest.xml 中设置 android:largeHeap=”true” 即可。这个设置效果还是很明显的,如需要加载一张原图,没有添加这个属性之前,使用Bitmap.setResources等方法时应用可能直接就 Crash 了,而增大应用可用内存后,加载一张原图可能就不会导致 crash 了(要加载大图?传送门Android 高清加载巨图方案 拒绝压缩图片)。

多进程

尽量不要使用多进程,因为多进程带来的问题可能比其带来的优化还要多,如果不了解,不熟悉,就不要使用。但是,当有需要的时候还是要考虑一下多进程,需要多进程时,就需要先了解下IPC。多进程如何实现,有什么坑,这里就不多做描述,毕竟总结起来比内存优化篇幅长多了。

2.2 节流

节流的含义是减少不必要的内存分配,从int还是long类型选择,到Bitmap加载图片,到是否需要将某个属性升级为成员变量,可以从各个方面去考虑、优化。下面列出几个可以优化的点:

  1. 采用合适的数据类型
  2. 使用 BitmapFactory.Options 加载合适尺寸大小和合适位深的图片
  3. 避免使用图片背景,如果必要,看看是否可以使用颜色背景和更小的图片一起替换
  4. 使用 ViewStub 延迟加载那些可能用不到的View
  5. 避免使用Activity作为全局变量,如果有必要,看看是否可以使用Application的context代替
  6. 如果需要,使用StringBuilder代替字符串拼接

2.3 复用

  1. 使用ViewHolder优化ListView ( RecyclerView代替ListView是一个不错的选择 )
  2. 使用三级缓存的图片加载工具显示图片,如Glide等

2.4 回收

不再需要的对象就要及时回收,一般来说GC会帮我们擦屁股,但是有些对象仍然需要主动释放,或者一些不规范的代码会导致对象无法被GC回收。

  1. 及时回收不再使用的Bitmap
  2. 根据需要修改象的引用类型:strong reference,SoftReference,WeakReference,PhantomReference
  3. 注意 Handler、线程、Timer 等异步工具类的使用,防止内存泄漏
  4. 及时关闭 closeable,如 InputStream 和 OutputStream,数据库的cursor等

三、内存问题检测与定位

有很多工具可以检测发现内存问题,静态代码分析工具 Lint,运行时发现代码问题的 StrictMode,专门用于内存泄漏检查的 LeakCanary。

3.1 Lint

Lint 是一个很好的代码分析工具,在我们编码阶段就可以给予我们很“友好”的优化提示,把 bug 拦截在编译之前。

Lint 是Android Studio 提供的 代码扫描分析工具,它可以帮助我们发现代码结构/质量问题,同时提供一些解决方案,而且这个过程不需要我们手写测试用例。

除了发现代码结构/质量问题外,Lint 还可以发现和删除没有使用的资源,从而减少apk的体积。Lint 的详细介绍和使用参考:Android 性能优化:使用 Lint 优化代码、去除多余资源

3.2 严格模式

StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的。举个例子来说,如果开发者在UI线程中进行了网络操作或者文件系统的操作,而这些缓慢的操作会严重影响应用的响应能力,甚至出现ANR对话框。为了在开发中发现这些容易忽略的问题,我们使用StrictMode,系统检测出主线程违例的情况并做出相应的反应,最终帮助开发者优化和改善代码逻辑。

严格模式的接入比较简单,但对于Android的性能优化(内存泄漏和ANR)有非常大的帮助,详情可以参考:Android严苛模式StrictMode使用详解

3.3 LeakCanary

在实际的项目中,其实没有用到LeakCanary,但是各种书,博客都在介绍 LeakCanary,想必也是一个很好的工具。LeakCanary的接入也不难,详情参考:LeakCanary 中文使用说明

3.4 Android Profiler 内存分析

Android Profiler 是 Android studio 3.0 版本代替原有的Android Monitor的性能分析工具。使用 Android Profiler分析内存泄漏请看手把手教你在Android Studio 3.0上分析内存泄漏,如果还在用以前版本看这里Android性能优化第(二)篇—Memory Monitor检测内存泄露

四、总结

导致 OOM 的问题大多是内存泄漏的问题,特别是Activity无法释放的问题尤为严重。通过各种工具分析定位,比较容易找到问题的代码,从而改正它。而除了内存泄漏,其它一些问题则是比较难发现,因为这些问题隐藏在代码间。要做到心中有数,就要有一个良好的编码规范,该分配创建对象时创建,该复用的复用,该回收时回收。内存优化是一条很远路,优化在每一行代码之间,只有时刻保持警觉,严格要求自己,才能写出健壮的代码。

参考

Android内存优化——常见内存泄露及优化方案

ANDROID内存优化(大汇总——中)

Andoird优化(二)内存优化点进来看看不会后悔的

Android 性能优化之内存泄漏检测以及内存优化(中)

Android性能优化之利用LeakCanary检测内存泄漏及解决办法

Android 性能优化:使用 Lint 优化代码、去除多余资源

Android严苛模式StrictMode使用详解

LeakCanary 中文使用说明

Android性能优化第(二)篇—Memory Monitor检测内存泄露

手把手教你在Android Studio 3.0上分析内存泄漏

原创文章 25 获赞 12 访问量 1万+

猜你喜欢

转载自blog.csdn.net/half_bottle/article/details/78598364