03 | 内存优化(上):4GB内存时代,再谈内存优化——学习总结

https://time.geekbang.org/column/article/71277
文章:内存优化杂谈

内存问题:异常(OOM 内存分配失败等),卡顿
改进方向两方面的工作:

优化RAM

即降低运行时内存。防止程序发生OOM异常,以及降低程序由于内存过大被LMK机制杀死的概率。

优化ROM

即降低程序占ROM的体积。这里主要是为了降低程序占用的空间,防止由于ROM空间不足导致程序无法安装。

移动设备发展

设备性能分级:
移动设备发展Facebook 有一个叫 device-year-class 的开源库。

2008 140MB RAM ,2018 Mate 20 Pro 8GB RAM。
phone memeory

误区: Native 内存不用管

Native 内存多了,物理内存不足,lmk 开始杀进程。
eg: bitmap 手动分配到 Native heap 并释放。

// 步骤一:申请一张空的 Native Bitmap
Bitmap nativeBitmap = nativeCreateBitmap(dstWidth, dstHeight, nativeConfig, 22);

// 步骤二:申请一张普通的 Java Bitmap
Bitmap srcBitmap = BitmapFactory.decodeResource(res, id);

// 步骤三:使用 Java Bitmap 将内容绘制到 Native Bitmap 中
mNativeCanvas.setBitmap(nativeBitmap);
mNativeCanvas.drawBitmap(srcBitmap, mSrcRect, mDstRect, mPaint);

// 步骤四:释放 Java Bitmap 内存
srcBitmap.recycle();
srcBitmap = null;

内存泄露的检测与修改


内存泄露

一. 内存泄露的监控方案

方法一: leakcanry

它通过弱引用方式侦查Activity或对象的生命周期,若发现内存泄露自动dump Hprof文件,通过HAHA库得到泄露的最短路径,最后通过notification展示。

  • dumphprof依然会造成应用明显的卡顿(SuspendAll Thread)。

通过对leakcanry做简单的定制,我们就可以实现以下一个内存泄露监控闭环。
_

方法二:DDMS

AndroidStudio 3.0中之后无法打开DDMS Google 放弃DDMS

  • 在SDK的 android-sdk/tools/ 路径下【就是和配置ADB命令一样的路径】有个monitor.bat 的批处理文件;

在这里插入图片描述
1、点击update heap 在你需要调试的process

2、 右边点击道heap的使用的情况,点击cause gc, 可以实时观看内存的情况

方法三:命令行

adb shell dumpsys meminfo <package_name|pid> [-d]

adb shell ps 找到对应的 pid or 包名

在这里插入图片描述

方法四:Allocation Tracker
  • 信息过于分散
  • 跟 Traceview 一样,无法做到自动化分析,每次手动 开始 、结束。
  • Allocation Tracking 时,不会对手机本身造成过多性能影响,但是停止时直到 数据 dump 出来之前,经常手机卡死甚至 ANR .
方法五:android studio Profiler

二. 对系统内存泄露的Hack Fix

AndroidExcludedRefs列出了一些由于系统原因导致引用无法释放的例子,同时对于大多数的例子,都会提供建议如何通过hack的建议去修复。在微信中,,,

三. 通过兜底回收内存

Activity泄漏会导致该Activity引用到的Bitmap、DrawingCache等无法释放,对内存造成大的压力,兜底回收是指对于已泄漏Activity,尝试回收其持有的资源,泄漏的仅仅是一个Activity空壳,从而降低对内存的压力。

做法也非常简单,在Activity onDestory时候从view的rootview开始,递归释放所有子view涉及的图片,背景,DrawingCache,监听器等等资源,让Activity成为一个不占资源的空壳,泄露了也不会导致图片资源被持有。

   …
   …
   Drawable d = iv.getDrawable();
   if (d != null) {
       d.setCallback(null);
   }        
   iv.setImageDrawable(null);
   ...
   ...

总的来说,我们不是只懂得一些内存泄露解决方法就可以,更重要的是通过日常测试与监控,得到内存泄露检测与修改的一整套闭环体系。

降低运行时内存的一些方法


当我们能确保应用中不会出现内存泄露时,我们需要一些其他的方法来降低运行时的内存。更多的时候,我们其实只希望降低应用发生OOM的概率。

一. 减少bitmap占用的内存

见下一章

二. 自身内存监控

对于系统函数 onLowMemory 等函数是针对整个系统而已的,对于本进程来说,其dalvik内存距离OOM的差值并没有体现,也没有回调函数供我们及时释放内存。

  • 实时监控进程的堆内存使用率,达到设定值即关于通知相关模块进行内存释放:
Runtime.getRuntime().maxMemory();  
  Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
  • 操作方式
    定期(前台每隔3分钟)去得到这个值,当我们这个值达到危险值时(例如80%),我们应当主要去释放我们的各种cache 资源(bitmap的cache为大头).
WindowManagerGlobal.getInstance().startTrimMemory(TRIM_MEMORY_COMPLETE)

三. 有节操的使用开进程

每个空进程也占用10MB 。
对于webview,图库等,由于存在内存系统泄露或者占用内存过多的问题,我们可以采用单独的进程。微信当前也会把它们放在单独的tools进程中 。

四. 上报OOM详细信息

当系统发生OOM的crash时 ,内存信息。large heap、 inBitmap、SparseArray。。。

GC优化


测试GC性能

例如暂停挂起时间、总耗时、GC 吞吐量,我们可以通过发送SIGQUIT 信号获得 ANR 日志。

adb shell kill -S QUIT PID
adb pull /data/anr/traces.txt

它包含一些 ANR 转储信息以及 GC 的详细性能信息:
sticky concurrent mark sweep paused:	Sum: 5.491ms 99% C.I. 1.464ms-2.133ms Avg: 1.830ms Max: 2.133ms     // GC 暂停时间

Total time spent in GC: 502.251ms     // GC 总耗时
Mean GC size throughput: 92MB/s       // GC 吞吐量
Mean GC object throughput: 1.54702e+06 objects/s 

一. GC的类型

  1. GC_FOR_ALLOC
    当堆内存不够的时候容易被触发,尤其是new一个对象的时候。可以提高dalvik.vm.heapstartsize的值,这样在启动过程中可以减少GC_FOR_ALLOC的次数。注意这个触发是以同步的方式进行的。
  2. GC_EXPLICIT
    这个gc是被可以调用的,比如system.gc, 一般gc线程的优先级比较低,所以这个垃圾回收的过程不一定会马上触发。
  3. GC_CONCURRENT
    当分配的对象大小超过384K时触发,注意这是以异步的方式进行回收的.如果发现大量反复的Concurrent GC出现,说明系统中可能一直有大于384K的对象被分配,而这些往往是一些临时对象,被反复触发了。给到我们的暗示是:对象的复用不够。
  4. GC_EXTERNAL_ALLOC (在3.0系统之后被废了)

二. 内存抖动现象

短时间内大量的对象被创建又马上被释放。
这个操作有可能会影响到帧率,并使得用户感知到性能问题。

三. GC优化

  1. 字符串拼接优化
    减少字符串使用加号拼接,改为使用StringBuilder。
    减少StringBuilder.enlarge,初始化时设置capacity;
logging.println(">>>>> Dispatching to " + msg.target + " " +
                msg.callback + ": " + msg.what);
  1. 读文件优化 读文件使用ByteArrayPool,初始设置capacity,减少expand.
  2. 资源重用
    建立全球缓存池,对频繁申请、释放的对象类型重用
  3. 减少不必要或不合理的对象
    例如在ondraw、getview中应减少对象申请,尽量重用。更多是一些逻辑上的东西,例如循环中不断申请局部变量等
  4. 选用合理的数据格式 使用 SparseArray, SparseBooleanArray, and LongSparseArray来代替Hashmap

总结

不拘泥于点,更重要在于如何建立合理的框架避免发生问题,或者是能及时的发现问题。

参考文章


Android内存管理(http://developer.android.com/intl/zh-cn/training/articles/memory.html)

leakcanary(https://github.com/square/leakcanary)

AndroidExcludedRefs(https://github.com/square/leakcanary/blob/master/leakcanary-android/src/main/java/com/squareup/leakcanary/AndroidExcludedRefs.java)

fresco(https://github.com/facebook/fresco)

优化安卓应用内存的神秘方法以及背后的原理(http://bugly.qq.com/blog/?p=621)

Android性能优化之内存篇(http://hukai.me/android-performance-memory/)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值