- 系统剩余内存。关于系统内存状态,可以直接读取文件 。当系统可用内存很小(低于 的 10%)时,、大量 、系统频繁自杀拉起等问题都非常容易出现。
- 应用使用内存。包括 内存、()、(),我们可以得出应用本身内存的占用大小和分布。
- 虚拟内存。虚拟内存可以通过 得到,通过 文件可以得到具体的分布情况。有时候我们一般不太重视虚拟内存,但是很多类似 、 等问题都是虚拟内存不足导致的。
一般来说,对于 32 位进程,如果是 32 位的 ,虚拟内存达到 3GB 就可能会引起内存申请失败的问题。如果是 64 位的 ,虚拟内存一般在 3~4GB 之间。当然如果我们支持 64 位进程,虚拟内存就不会成为问题。因此我们的应用应该尽量适配64位
资源信息
有的时候我们会发现应用堆内存和设备内存都非常充足,还是会出现内存分配失败的情况,这跟资源泄漏可能有比较大的关系。
- 文件句柄 。一般单个进程允许打开的最大文件句柄个数为 。但是如果文件句柄超过 个就比较危险,需要将所有的 以及对应的文件名输出到日志中,进一步排查是否出现了有文件或者线程的泄漏
- 线程数。一个线程可能就占 的虚拟内存,过多的线程会对虚拟内存和文件句柄带来压力。根据我的经验来说,如果线程数超过 400 个就比较危险。需要将所有的线程 以及对应的线程名输出到日志中,进一步排查是否出现了线程相关的问题。
应用信息
除了系统,其实我们的应用更懂自己,可以留下很多相关的信息。
- 崩溃场景。崩溃发生在哪个 或 ,发生在哪个业务中。
- 关键操作路径。不同于开发过程详细的打点日志,我们可以记录关键的用户操作路径,这对我们复现崩溃会有比较大的帮助。
- 其他自定义信息。不同的应用关心的重点可能不太一样,比如网易云音乐会关注当前播放的音乐,QQ 浏览器会关注当前打开的网址或视频。此外例如运行时间、是否加载了补丁、是否是全新安装或升级等信息也非常重要。
上面介绍了在崩溃现场应该采集的信息,当然开发一个这样的采集平台还是很复杂的,大多数情况我们只需要接入一些第三方的平台比如和即可。但是通过上述介绍,我们可以知道在分析崩溃的时候应该重点关注哪些信息,同时如果平台能力有缺失,我们也可以添加自定义的上报
崩溃分析
在崩溃现场上报了足够的信息之后,我们就可以开始分析崩溃了,下面我们介绍崩溃分析“三部曲”
第一步:确定重点
确认和分析重点,关键在于在日志中找到重要的信息,对问题有一个大致判断。一般来说,我建议在确定重点这一步可以关注以下几点。
- 确认严重程度与优先级。解决崩溃也要看性价比,我们优先解决 崩溃或者对业务有重大影响,
- 崩溃基本信息。确定崩溃的类型以及异常描述,对崩溃有大致的判断。一般来说,大部分的简单崩溃经过这一步已经可以得到结论。
- 崩溃。 崩溃类型比较明显,比如 是空指针, 是资源不足,这个时候需要去进一步查看日志中的 “内存信息”和“资源信息”。
- 崩溃。需要观察 、、 等内容,以及崩溃时 的堆栈。关于各 含义的介绍,你可以查看 崩溃信号介绍 。比较常见的是有 和 ,前者一般是由于空指针、非法指针造成,后者主要因为 和调用 退出所导致。
- 。我的经验是,先看看主线程的堆栈,是否是因为锁等待导致。接着看看 日志中 、、、 等信息,进一步确定是 问题,或是 竞争问题,还是由于大量 导致卡死
- 。 一般会存在一些有价值的线索,日志级别是 、 的需要特别注意。从 中我们可以看到当时系统的一些行为跟手机的状态,例如出现 时,会有“am_anr”; 被杀时,会有“am_kill”。不同的系统、厂商输出的日志有所差别,当从一条崩溃日志中无法看出问题的原因,或者得不到有用信息时,不要放弃,建议查看相同崩溃点下的更多崩溃日志。
- 各个资源情况。结合崩溃的基本信息,我们接着看看是不是跟 “内存信息” 有关,是不是跟“资源信息”有关。比如是物理内存不足、虚拟内存不足,还是文件句柄 泄漏了。
无论是资源文件还是 ,内存与线程相关的信息都需要特别注意,很多崩溃都是由于它们使用不当造成的。
第二步:查找共性
如果使用了上面的方法还是不能有效定位问题,我们可以尝试查找这类崩溃有没有什么共性。找到了共性,也就可以进一步找到差异,离解决问题也就更进一步。
机型、系统、、厂商、,这些采集到的系统信息都可以作为维度聚合,共性问题例如是不是因为安装了 ,是不是只出现在 的手机,是不是只有三星这款机型,是不是只在 的系统上。应用信息也可以作为维度来聚合,比如正在打开的链接、正在播放的视频、国家、地区等。找到了共性,可以对你下一步复现问题有更明确的指引。
第三步:尝试复现
如果我们已经大概知道了崩溃的原因,为了进一步确认更多信息,就需要尝试复现崩溃。如果我们对崩溃完全没有头绪,也希望通过用户操作路径来尝试重现,然后再去分析崩溃原因。
“只要能本地复现,我就能解”,相信这是很多开发跟测试说过的话。有这样的底气主要是因为在稳定的复现路径上面,我们可以采用增加日志或使用 、 等各种各样的手段或工具做进一步分析。
系统崩溃解决
有时有些崩溃并不是我们应用的问题,而是系统的问题,系统崩溃系统崩溃常常令我们感到非常无助,它可能是某个 版本的 ,也可能是某个厂商修改 导致。
这种情况下的崩溃堆栈可能完全没有我们自己的代码,很难直接定位问题。
针对这种疑难问题,我们可以尝试通过以下方法解决。
- 查找可能的原因。通过上面的共性归类,我们先看看是某个系统版本的问题,还是某个厂商特定 的问题。虽然崩溃日志可能没有我们自己的代码,但通过操作路径和日志,我们可以找到一些怀疑的点。
- 尝试规避。查看可疑的代码调用,是否使用了不恰当的 ,是否可以更换其他的实现方式规避。
- 解决。在了解了原因之后,最后可以通过的方式修改系统代码的逻辑来处理
比如我们发现线上出现一个 相关的系统崩溃,它只出现在 的系统中,看起来是在 显示的时候窗口的 已经无效了。这有可能出现在 需要显示时,窗口已经销毁了。
为什么 的系统不会有这个问题?在查看 的源码后我们发现有以下修改:
因此我们可以参考 的做法,直接 住这个异常。这里的关键在于寻找 点, 里面有一个变量叫 ,它的类型为 ,我们只需要代理它就可以实现捕获。
长效治理
上面介绍了处理线上的一般步骤,但是治理真正重要的阶段在上线之前,我们需要从开发阶段开始,系统性的进行长效治理
开发阶段
的长效治理需要从开发阶段抓起,从长远来说,更好的代码质量将带来更好的稳定性,我们可以从以下两个角度来提升代码质量
- 统一编码规范、增强编码功底、技术评审、增强机制
- 架构优化,能力收敛(即将一些常见的操作进行封装),统一容错:如在网络库utils中统一对返回信息进行预校验,如不合法就直接不走接下来的流程。
测试阶段
除了功能测试、自动化测试、回归测试、覆盖安装等常规测试流程之外,还需要针对特殊场景、机型等边界进行测试:如服务端返回异常数据、服务端宕机等情况
合码阶段
- 在我们的功能开发完毕,即将合并到主分支时,首先要进行编译检测、静态扫描,来发现可能存在的问题。
- 在扫描完成后也不能直接合入,因为多个分支可能会冲突,因此我们先进行一个预编译流程,即合入一个与主分支一样的分支、然后打包进行主流程自动化回归测试,流程通过后再合入主分支。当然这样做可能比较麻烦,但这些步骤应该都是自动化的
发布阶段
- 在发布阶段,我们应该进行多轮灰度,灰度量级应逐渐由小变大,用最小的代价提前暴露问题
- 灰度发布同样应该分场景、多纬度全面覆盖,可以针对特别的版本,机型等进行专门的灰度,看下那些更有可能出现问题的用户是否出现问题
运维阶段
- 在上线之后,稳定性问题同样需要关注,因此特别依赖于的灵敏监控,发现问题及时报警
- 如果出现了异常情况,也需要根据情况进行回滚或者降级策略
- 如果不能回滚或降级的话,也可以采用热修复的方式来修复、如果热修复也失效的话,只能依赖于本地容灾方案来恢复
业务高可用方案建设
很多人认为稳定性优化就是降低率,但其实稳定性优化还有一个重要的维度就是业务的高可用。
业务的不可用可能不会导致崩溃,但是会降低用户的体验,从而直接影响我们的收入
业务高可用方案建设
- 业务高可用不像,需要我们自己打点做数据采集。我们需要梳理项目主流程、核心路径、关键节点,并添加打点
- 数据采集我们也可以采用方式采集,减少手动打点的成本。
- 数据上报之后,我们可以建立数据大盘,统计每个步骤的转化率。
- 在数据之报之后,我们也可以建立报警策略,比如阈值报警、趋势报警(相比同期减少)、特定指标报警(比如支付失败)
- 同时我们可以做一些异常监控的工作,比如住的异常与异常逻辑的上报,这些异常虽然不会崩溃,但也是我们需要关注的
- 针对一些难以解决的问题,我们可以针对特定用户采用全量日志回捞的方式来采集更多信息,
- 在发现了异常之后,我们可以通过一些兜底策略来解决问题,比如支持通过配置中心配置功能开关是否打开,当发现某个新功能有问题时,我们可以直接隐藏该功能,或者通过配置路由的方式跳转到另一个方式
客户端容灾方案
在性能或者业务异常发生了之后,我们该如何解决呢?传统的流程需要经过用户反馈,重新打包,渠道更新等多个步骤,可以看出其实比较麻烦,对用户的响应度也比较低
我们可以从以下角度来进行客户端的容灾方案建设
- 对于新加的功能或者代码重构,支持通过配置中心配置开关,如果发生问题可以及时关闭
- 同时如果我们的所有的页面都是通过路由跳转的,可以通过动态配置路由的方式跳转到统一错误处理页面,或者跳转到临时h5页面
- 通过热修复技术修复,比如接入腾讯的或者美团的等
- 如果你的项目使用了或者,可直接实现增量更新
- 如果崩溃发生在刚启动时,这时候动态更新动态配置就都失效了,这个时候就需要用到安全模式。安全模式根据信息自动恢复,多次启动失败重置应用为安装初始状态。如果是特别严重的,也可以通过阻塞性热修复的方式来解决,即热修成功了才能进入。安全模式不仅可以用于,也可用于组件,如果某个组件多次报错,就可以进入兜底页面
稳定性优化常见面试题
下面介绍一下稳定性优化的模拟面试题
你们做了哪些稳定性方面的优化?
随着项目的逐渐成熟,用户基数逐渐增多,持续升高,我们遇到了很多稳定性方面的问题,对于我们技术同学遇到了很多的挑战,用户经常使用我们的卡顿或者是功能不可用,因此我们就针对稳定性开启了专项的优化,我们主要优化了三项:
- 专项优化
- 性能稳定性优化
- 业务稳定性优化
通过这三方面的优化我们搭建了移动端的高可用平台。同时,也做了很多的措施来让真正地实现了高可用。
性能稳定性是怎么做的?
- 全面的性能优化:启动速度、内存优化、绘制优化
- 线下发现问题、优化为主
- 线上监控为主
- 专项优化
我们针对启动速度,内存、布局加载、卡顿、瘦身、流量、电量等多个方面做了多维的优化。
我们的优化主要分为了两个层次,即线上和线下,针对于线下呢,我们侧重于发现问题,直接解决,将问题尽可能在上线之前解决为目的。而真正到了线上呢,我们最主要的目的就是为了监控,对于各个性能纬度的监控呢,可以让我们尽可能早地获取到异常情况的报警。
同时呢,对于线上最严重的性能问题性问题:,我们做了专项的优化,不仅优化了的具体指标,而且也尽可能地获取了发生时的详细信息,结合后端的聚合、报警等功能,便于我们快速地定位问题。
业务稳定性如何保障?
参考答案:
- 数据采集 + 报警
- 需要对项目的主流程与核心路径进行埋点监控,
- 同时还需知道每一步发生了多少异常,这样,我们就知道了所有业务流程的转换率以及相应界面的转换率
- 结合大盘,如果转换率低于某个值,进行报警
- 异常监控 + 单点追查
今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
【Android核心高级技术PDF文档,BAT大厂面试真题解析】
【算法合集】
【延伸Android必备知识点】
【Android部分高级架构视频学习资源】
**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!