Android开发高手课之内存优化

内存问题

  1. 异常
  2. 卡顿 :Java内存不足会导致频繁GC
//通过发送SIGQUIT信号获得ANR日志
adb shell ps -l //查看进程pid
adb shell kill -S QUIT PID
adb pull /data/anr/traces.txt

Android Bitmap内存分配的变化

  1. Android 3.0之前,Bitmap放在Java堆,而像素数据放在Native内存中,如果不手动调用recycle,Bitmap Native内存的回收完全依赖finalize函数回调。
  2. Android 3.0 ~ Android 7.0将Bitmap对象和像素数据统一放到Java堆中,这样即使不调用recycle,Bitmap内存也会随着对象一起被回收。造成的问题是,第一Bitmap内存消耗太大,第二会引起大量的GC。
  3. Android 8.0将Bitmap内存放到Native中,使用NativeAllocationRegistry来辅助回收Native内存,可以做到和对象一起释放,8.0还增加了硬件位图Hardware Bitmap,可以减少图片内存并提升绘制效率。

误区:Native内存不用管

将Bitmap放到native内存中的黑科技

//申请一张空的Native Bitmap
//通过调用libandroid_runtime.so中的Bitmap构造函数,可以得到一张空的Bitmap对象,而它的内存是放到Native堆中。不同版本有差异,需要适配
Bitmap nativeBitmap = nativeCreateBitmap(dstWidth, dstHeight, nativeConfig, 22)

//申请一张普通的Java Bitmap
Bitmap srcBitmap = BitmapFactory.decode(res, id)

//使用java Bitmap将内容绘制到Native Bitmap中
mNativeCanvas.setBitmap(nativeBitmap)
mNativeCanvas.drawBitmap(srcBitmap, mSrcRect, mDstRect, mPaint)

//释放Java Bitmap内存
srcBitmap.recycle()
srcBitmap = null;

以上方法存在的问题:

  1. 兼容性问题,需要适配
  2. 频繁申请释放Java Bitmap容易导致内存抖动

内存测量方法

adb shell dumpsys meminfo  [-d]

Java内存分配

工具:

  1. Allocation Tracker
    1. 获取的信息过于分散,中间夹杂着其他的信息
    2. 无法做到自动化分析,每次都需要开发者手动开始 / 结束
    3. 在停止时,直接把数据dump出来之前,会造成手机卡死
  2. MAT

Native内存分配

Native内存调试的两种方法

  1. Malloc调试:可以调试Native内存的一些使用问题
adb shell setprop wrap. ' "LIBC_DEBUG_MALLOC_OPTIONS=backtrace logwrapper" '
  1. Malloc 钩子:Android P之后,Android的libc支持拦截在程序运行期间发生的所有分配 / 释放调用。
adb shell setprop wrap. ' "LIBC_HOOKS_ENABLE=1" '

设备分级

  1. 使用device-year-class的策略对设备分级,对于低端机用户可以关闭复杂动画或者某些功能,使用565格式的图片,使用更小的缓存内存等。
  2. 缓存管理,使用onTrimMemory回调,根据不同的状态决定释放多少内存,统一缓存管理可以更好的监控每个模块的缓存大小。
  3. 进程模型,减少应用启动的进程数,减少常驻进程
  4. 安装包大小,安装包中的代码、资源、图片以及so库的体积,跟他们占用的内存有很大的关系。

Bitmap优化

  1. 统一图片库
  2. 统一监控
    1. 大图片监控:需要注意某张图片内存占用是否过大
    2. 重复监控:Bitmap像素数据完全一致的图片
    3. 图片总内存:统计应用所有图片占用的内存

内存泄漏

  1. Java内存泄漏
  2. OOM监控
  3. Native内存泄漏监控
  4. 针对无法重编so的情况
  5. 针对可重编的so情况

内存监控

  1. 采集方式

可以每5分钟采集一次PSS、Java堆、图片总内存,采样部分用户

  1. 计算指标

内存异常率:可以反映内存占用的异常情况,PSS值可以通过Debug.MemoryInfo拿到

内存UV异常率 = PSS超过400MB的UV / 采集UV

触顶率:可以反映java内存的使用情况

内存UV触顶率 = Java堆占用超过最大堆限制的85%的UV / 采集UV

计算是否触顶的方法

long javaMax = runtime.maxMemory()
long javaTotal = runtime.totalMemory()
long javaUsed = javaTotal - runtime.freeMemory()
float proportion = (float)javaUsed / javaMax

内存分配次数和大小

Debug.getRuntimeStat("art.gc.gc-count")
Debug.getRuntimeStat("art.gc.gc-time")
Debug.getRuntimeStat("art.gc.blocking-gc-count")
Debug.getRuntimeStat("art.gc.blocking-gc-time")

Dalvik日志消息

D/dalvikvm: , , ,

D/dalvikvm( 9050): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K, paused 2ms+2ms

垃圾回收原因

  1. GC_CONCURRENT:在堆开始占用内存时可以释放的内存的并发垃圾回收
  2. GC_FOR_MALLOC:堆已满而系统不得不停止您的应用程序并回收内存,您的应用尝试分配内存而引起的垃圾回收
  3. GC_HPROF_DUMP_HEAP:当请求创建HPROF文件来分析堆时出现的垃圾回收
  4. GC_EXPLICIT:显示垃圾回收,调用gc()时
  5. GC_EXTERNAL_ALLOC:仅适用于API级别10以及以下,外部分配内存的垃圾回收。

释放量

从此次垃圾回收中回收的内存量

堆统计数据

堆的可用空间百分比与(活动对象数量) / (堆总大小)

外部内存统计数据

API级别10及更低级别的外部分配内存 (已分配内存量) / (发生回收的限制)

暂停时间

堆越大,暂停时间越长,并发暂停时间显示了两个暂停,一个出现在回收开始时,一个出现在回收快要完成时。

ART日志消息

I/art: ( 释放大小) AllocSpace Objects, ( 释放的大型对象大小) LOS objects,

I/art : Explicit concurrent mark sweep GC freed 104710(7MB) AllocSpace objects, 21(416KB) LOS objects, 33% free, 25MB/38MB, paused 1.230ms total 67.216ms

原因

  1. Concurrent:不会暂停应用线程的并发垃圾回收
  2. Alloc:在堆已满时尝试分配内存引起的垃圾回收
  3. Explicit:调用gc()产生的垃圾回收。
  4. NativeAlloc:Native层出现的内存回收,如位图或RenderScript分配对象
  5. CollectorTransition:堆转换引起的回收
  6. HomogeneousSpaceCompact:空间压缩,减少RAM使用量并对堆进行碎片整理。
  7. DisableMovingGc
  8. HeapTrim

名称

  1. Concurrent mark sweep (CMS):会释放和回收映像空间以外的所有其他空间
  2. Concurrent partial mark sweep:会回收除了映像空间和 zygote 空间以外的所有其他空间。
  3. Concurrent sticky mark sweep:只释放上次垃圾回收之后分配的对象,次垃圾回收比完整或部分垃圾清除运行的更频繁,因为更快速且暂停时间更短。
  4. Marksweep + semispace:对堆进行碎片整理

释放的对象

此次垃圾回收从非大型对象空间回收的对象数量

释放的大小

此次垃圾回收从非大型空间回收的字节数量

释放的大型对象

此次垃圾回收从大型对象空间回收的对象数量

释放的大型对象大小

此次垃圾回收从大型对象空间回收的字节数量

堆统计数据

空闲百分比 (活动对象数量)/ (堆总大小)

暂停时间

通常情况下,暂停时间与垃圾回收运行时修改的对象引用数量成正比。

扩展阅读

https://developer.android.com/studio/profile/investigate-ram?hl=zh-cn

https://source.android.com/devices/tech/debug/native-memory

https://mp.weixin.qq.com/s/b_lFfL1mDrNVKj_VAcA2ZA?

你可能感兴趣的:(Android开发高手课之内存优化)