Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,经过平常工做经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处。java
在业内,Android 手机一直有着“越用越慢”的口碑。根据第三方的调研数据显示,有77%的 Android 手机用户认可本身曾遭遇过手机变慢的影响。他们不明白为何购买之初“如丝般顺滑”的 Android 手机,在使用不到一年以后都会“卡顿”得让人抓狂!根据咱们初步的测试数据,手机长期所使用产生的磁盘碎片可使得磁盘的写入效率降低为原来的50%。是否是有一种“吓死本宝宝了”的感受。缓存
那么怎么办呢?笔者曾经对这一问题进行分析,且让我一一贯你道来。markdown
故事的原由是,针对“Android 系统越用越卡的问题”,腾讯某产品团队但愿在自身产品中进行优化,从而提高产品口碑。网络
通过简单的分析讨论,你们认为形成这种现象多是因为两个方面缘由:内存、磁盘:架构
先说内存。为了保证应用能够快速被再次调起,Android 在内存管理上采用以下策略:进程保持在内存中,在占用内存未超过阈值以前不会系统进行主动清理。但随着应用的增多,试图保持在内存中的进程将会增多,所以影响系统的流畅度。能够说,内存与系统卡顿的关系早已经是业界的共识,其解决方案也比较明了,即赋予系统主动清理内存的能力,例如待机后杀掉没必要要的进程。app
再聊磁盘。长期使用 Android 手机必将产生大量的磁盘碎片,而磁盘碎片将会下降磁盘的读写性能,从而影响系统流畅度。可是磁盘碎片是否能对磁盘的读写性能形成了很大影响,以致于影响系统流畅度还没有可知,且暂时也没有发现能够进行尝试的潜在优化点。工具
因而,产品团队找到了咱们专项测试组,但愿分析 Android 越用越卡与磁盘是否有关系,并初步探索系统在磁盘管理模式方面是否存在潜在优化点。这就有了下文。性能
其实,平常的生活经验(例如 SSD 可让老笔记本焕发新生)已经咱们可以感受获得磁盘对系统流畅度的影响很大。可是,这里仍是有再必要简单说一下,磁盘是如何影响系统流畅度的。学习
开发过 Android 项目的同窗都知道 Android 在使用网络的最佳实践是使用3级缓存的设计来提高系统的流畅度并节省流量:CPU 首先尝试从内存中加载图片,若此时图片存在在内存中则加载成功,不然内存会从磁盘中加载图片,若此时图片存在在磁盘中则加载成功,不然磁盘会最终向网络中下载图片。测试
其实上述的执行逻辑,也就解释了磁盘是如何影响系统流畅度的:对于系统流畅度(其实也是各个应用的流畅度)影响最直接的就是 CPU 的执行效率,可是若是这个过程当中内存、磁盘以及网络的读写速度若是跟不上 CPU 的执行效率的话,就会形成 CPU 在处理任务的时候须要花费时间等待数据,从而影响了流畅度。
因此第一个问题就弄清楚了:磁盘的读写速度的下降会使得系统流畅度变差!那么,咱们要分析的问题就转化成:磁盘在长期使用的过程当中,其读写速度会不会下降。
为了分析清楚磁盘“磁盘在长期使用的过程当中,其读写速度会不会下降”这个问题,咱们有必要先弄明白 Android 磁盘所采用的读写机制。
经过资料查阅,咱们了解到目前,Android 手机大多采用 NAND Flash 架构的闪存卡来存储内容。NAND Flash 的内部存储单位从小到大依次为:Page、Block、Plane、Die,而一个 Device 上能够封装若干个 Die。下图就是一个 NAND Flash 组成结构的示意图。
为了方便理解,针对一个 Die,咱们再抽象一下,Page、Block、Plane、Die 的关系以下图所示。
虽然 NAND Flash 的优势多多,可是为了延长驱动器的寿命,它的读写操做均是以 Page 为单位进行的,但擦除操做倒是按 Block 为单位进行的。
因为有大量的读写操做,因而咱们的 NAND Flash 制定了以下的读写规则:
那么问题来了!假如如今我要向磁盘中写入一张图片的数据,这个图片的数据大小恰好为一个 Page。最坏的状况就是,内存中刚好只有一个 Block 刚好有一个 Page 的无效数据能够擦除。为了存下这张图片,因而主控就把这个 Block 的全部数据读至缓存,擦除Block上的内容,再向缓存中加上这个4KB 新数据后最后写回 Block 中。
个人天啊,其实想存储的就是1个 Page 的图片内容,可是实际上确形成了整个 Block 的内容都被从新写入,同时本来简单一步搞定的事情被还被分红了先后四步执行(闪存读取、缓存改、闪存擦除、闪存写入)形成延迟大大增长,速度变慢。这就是传说中的“写入放大”(Write Amplification)问题。而“写入放大”也说明了磁盘在长期使用的过程当中,其读写速度(尤为是写入速度)会存在下降的现象。
不过,既然“写入放大”(Write Amplification)都这么出名了,确定不会没有现成的解决方案的!这个很简单,Google 一下,咱们就知道解决方案就是 TRIM 技术。
TRIM 是一条 ATA 指令,由操做系统发送给闪存主控制器,告诉它哪些数据占的地址是“无效”的。在 TRIM 的帮助下,闪存主控制器就能够提早知道哪些 Page 是“无效”的,即可以在适当的时机作出优化,从而改善性能。这里要强调下,TRIM 只是条指令,让操做系统告诉闪存主控制器这个 Page 已经“无效”就算完了,并无任何其它多余的操做。在测试的过程当中,咱们发现 TRIM 的触发须要操做系统、驱动程序以及闪存主控三者都支持才能真正意义上实现。例如:
基于 TRIM 技术,目前常见有两种方案能够解决“写入放大”的问题:
不得不说,若是从用户的角度出发,仍是 FSTRIM 的方法更靠谱一些,但如何寻找合适的 TRIM 时机就是一个比较讲究的问题了。
根据前面的分析,咱们不难理解在 Android 中的 TRIM 选择经过 fstrim 命令的方式进行实现。那么,Google 又是如何设计触发TRIM的时机呢?
经过走读 Android 源码(AOSP 4.4.4),能够了解到 Android 经过系统服务 IdleMaintenanceService 来进行系统状态监控并决定什么时候触发 TRIM。根据 IdleMaintenanceService.java 源码,咱们绘制了 fstrim 的触发示意图以下:
注释:
- 有/无操做:距屏幕熄灭||屏保启动已超过71分钟
- 是/否电量充足:维护期20%,非维护期(充电状态30%,非充电状态80%)
- 是/否维护超时:启动维护已超过71分钟
- 是/否已到维护期:据上次启动维护超过1天
了解了这么多技术背景,那咱们经过测试数据分析闪存碎片和 TRIM 对磁盘 I/O 性能的影响。根据测试目的,具体的测试设置以下:
评估闪存碎片和TRIM对磁盘 I/O 性能的影响
测试对象:LG Nexus 5 with cm-11-20140805-SNAPSHOT-M9-hammerhead
测试步骤:
备注:
数据解读:
完成了上面的工做,不禁得让咱们大吃一鲸:原来 TRIM 对 SD 卡的读写速度的维护如此重要!前面也说到,Android 选择 FSTRIM 方案的来实现 TRIM,那么 Android 所设计的 FSTRIM 触发时机有没有什么问题呢?
根据 Android 系统的设定,FSTRIM 预期是每隔24小时触发一次。因此,接下来咱们须要评估一下,FSTRIM 可否依据上述设定成功被系统触发。
分析 FSTRIM 可否被按时被系统触发
测试对象:2台 Samsung Galaxy Nexus 及2台 LG Nexus 5
测试步骤:
开启WiFi | 关闭WiFi | |
---|---|---|
Samsung Galaxy Nexus | 启动FSTRIM 1次 | 启动FSTRIM 1次 |
LG Nexus 5 | 未启动FSTRIM | 启动FSTRIM 1次 |
数据解读:
测试数据显示 FSTRIM 大多数状况会被自动触发,但也存在没法触发的状况。可能的缘由是:FSTRIM 对电量的要求略高,因此一旦发生意外状况(如应用的 PUSH 消息)终止了计划 FSTRIM 的执行以后,很长时间以内都没法再知足 FSTRIM 的启动条件。
因此,如需提升其触发频率,咱们能够考虑下降触发条件中对电量的要求。
根据前面的分析,咱们能够从 Android 源码及测试数据对前面两个问题作出回答:
固然,咱们能够经过一下手段对这一问题作出优化尝试:
至此,咱们也大体解答了项目组提出的问题,这个故事也基本能够告一段落了。
回顾整个分析问题的过程,我发现,做为一名专项测试人员,尽管我并不须要实际编写项目中的任何一句代码,但这并不意味着我不须要了解 Android 及其 Framework 的代码。实际上,只有在平时的学习和工做中了解其工做机制的基础上,咱们才能设计出合理的测试方案,从而更好 的完成工做。
腾讯 Bugly 是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的状况以及解决方案。智能合并功能帮助开发同窗把天天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同窗定位到出问题的代码行,实时上报能够在发布后快速的了解应用的质量状况,适配最新的 iOS, Android 官方操做系统,鹅厂的工程师都在使用,快来加入咱们吧…