本系列文章根据手机淘宝客户端基础架构高级开发工程师非台在安卓绿色联盟开发者大会上的分享,共分三篇,介绍手淘技术团队性能和稳定性系统化提升方案EMAS-MOTU的设计原理以及实现思路。
本文将重点介绍手淘高可用平台如何进行性能及稳定性治理。
性能治理
主线程卡顿
主线程卡顿是因为主线程的消息超过阈值,从而导致页面丢帧。手淘通过接管主线程消息的分发机制,获取消息的分发耗时和消息类型,从而定位触发主线程卡顿的具体业务并进行针对性治理。
另外手淘在使用系统的SharedPreferences时,发现页面跳转导致界面ANR的情况。通过阅读系统源码,发现它在做Receiver或者Service时会强制把所有SharedPreferences apply的内容写入文档,导致ANR。针对这个问题,手淘重写SharedPreferences提升性能,减少了这类ANR问题的发生。
内存泄漏
手淘团队投入了大量时间对内存泄露进行治理。一方面通过接管系统底层组件的生命周期,当组件的生命周期销毁时,对它进行一个WeakReference的引用,然后根据GC事件触发情况来判定该对象是否泄漏。另一方面,在Native层通过Hook操作系统底层malloc和free方法,计算每一个so处理内存的情况,根据malloc与free差值大小与白名单进行对比判断是否存在内存泄露。
内存使用不当
何为内存使用不当?举个例子,当开发一个大小为100×100 view,实际却使用了200*200甚至更大的bitmap。比如在系统drawable目录下放置一张图片,在高清的设备上展示时,它会根据系统自身的原理对它进行拉伸。这时原本只需要单位1的内存(100x100),可能变成单位16的内存(400x400),内存的浪费率达到90%以上。针对这种情况,手淘做了一个内存使用不当排查插件。
视频也同样存这种问题,在低分辨率设备上播放高质量的视频不仅不会给用户带来更好的体验,还可能让设备出现卡顿。还有就是图片持有的问题,当页面已经沉入栈底,最好不要保留之前页面的图片。这样可以保证有足够的内存给前台页面使用,否则随着页面层级的深入,很容易出现OOM。
资源泄露
手淘主要通过接管系统底层的open和close两个Native方法函数对资源泄露进行治理。当open和close没有成对出现,并且该业务并不是伴随整个应用生命周期(伴随整个生命周期的文件有白名单),可以判断该操作可能存在资源泄漏。平台会将该异常行为告知对应的开发同学检查和治理。数据库的治理同样也采用了这套方案。
线程问题
线程问题比较复杂。在线程创建时可能会触发一些意想不到的问题,比如Out Of Memory error。Out Of Memory error可能是由线程创建失败导致的。因此,手淘对线程创建进行了接管。业务在创建时,对它的方法调用栈进行聚类,就可以知道每个业务创建的线程数量,以及线程创建是否合理。建议应用开发者在创建线程时一定规范命名,以便快速定位具体的业务方。
流量监控
手淘主要是通过接管Socket协议,分析协议头部获取请求和回流数据内容的大小信息进行流量监控治理。如发现异常,可以让开发同学定位解决。同时也可以监听后台流量行为,观察APP切到后台以后是否还有大量的网络请求。
设备评级
安卓设备百花齐放,手淘对不同的设备采取了计分的方法进行评级,根据设备分数采用不同的策略,展示相应的图片、视频和业务,给用户带来最佳的性能体验。这个设备评级方案可以给开发同学提供指导建议,更好的展现业务形态。
布局性能
在开发的过程中,常常要通过HierarchyViewer的方法检查布局结构是否合理。手淘写了一套算法,检查页面结构是否合理,页面层级是否过深、页面层级是否还有继续优化的空间。同时还实现了一套OverDraw的算法,给开发同学提示具体哪个层级可以优化,怎么样的降层级,怎么样解决OverDraw的问题。
用户体验优化
手淘很关注用户的体验,包括启动时间、每个页面的打开耗时等。通过监控启动时各个子任务的耗时,以及对这些信息快速的分析,判断每一次发版质量变化的具体原因。
4.X设备体验优化
随着产品功能越来越丰富和产品体积逐渐的壮大,4.X设备出现了multidex越来越慢的情况。基于这个现象,手淘把谷歌的support multidex包进行了重构改造。经过重构以后的support multidex方案,4.X设备的Dex加载随着Dex越来越多,它性能提升越好。
内存容灾
内存容灾手淘一直很关注。当用户使用当前页面时,如果内存不足,手淘期望后台页面可以快速释放内存资源,为前台页面服务。手淘开发了内存容灾插件,监听JVM的GC事件以及轮寻物理页表计算实际使用物理内存,通过这个计算给手淘的业务方发送对应的内存水位事件,如果是属于非常高危的内存事件,就可以让后台快速的释放缓存资源,从而为可视的页面提供更好的服务。
稳定性治理
稳定性的治理主要是两部分,Java Crash和Native Crash。
Java Crash治理
手淘通过接管UncaughtExceptionHandler,拿到具体的Java Crash的信息及它的堆栈来进行Java Crash治理。Java Crash治理还有一个经常遇到的OOM的情况,一方面可能是虚拟机的内存不足导致的,另一方面可能是线程创建失败导致的,可以使用前面讲到的线程创建插件解决。
Native Crash治理
手淘通过捕获信号量的方式对Native Crash进行治理。当Native发生异常时,创建一个子进程,通过ptrace的方式去dump Native Crash上下文的线程信息。 基于前面讲到的性能治理方式,OOM的Native Crash可以通过malloc和free的接管,定位具体是哪个SO导致这个问题的发生并进行治理。
下期分享将重点介绍EMAS-MOTU热修复方案以及开发流程,敬请期待!