西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里( 四 )


# 系统异步 UI 泄漏
根据上传聚合的引用链我们发现在 Android 6.0 以下有一个 HandlerThread 作为 GCROOT 持有大量 Activity
导致内存泄漏,根据引用发现这些泄漏的 Activity 都被一个 Runnable(这里是 Runnable 是一个系统事件
SendViewStateChangedAccessibilityEvent)持有,这些 Runnable 被添加到一个 RunQueuel
中,这个队列本身被 TheadLocal 持有 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

图 12. HandlerThread 泄露链路
我们从 SendViewStateChangedAccessibilityEvent 入手对源码进行了分析发现它在
notifyViewAccessibilityStateChangedIfNeeded 中被抛出,系统的大量 view 都会在自身的一些 UI 方法(eg:
setChecked)中触发该函数 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

SendViewStateChangedAccessibilityEvent 的 runOrPost 方法会走到我们常用的 View 的 postDelay
方法中,这个方法在当 view 还未被 attched 到根 view 的时候会加入到一个 runQueue 中 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

这个 runQueue 会在主线程下一次的 performTraversals() 中消费掉 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

如果这个 runQueue 不在主线程那就没有消费的机会 。
根据上面的分析发现造成这种内存泄漏需要满足一些条件:
1. view 调用了 postDelay 方法 (这里是 notifyViewAccessisbilityStateChangeIfNeeded 触发)
2. view 处于 detached 状态
3. 上述过程是在非主线程里面操作的,ThreadLocal 非 UIThread,持有的 runQueue 不会走 performTraversals 消费掉 。
3.
抖音这边大量使用了异步 UI 框架来优化渲染性能,框架内部由一个 HandlerThread 驱动,完全符合上述条件 。针对该问题,我们通过反射获取非主线程的
ThreadLocal,在每次异步渲染完主动清理内部的 RunQueue 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

图 13. 反射清理流程
另外,Google 在 6.0 上也修复了 notifyViewAccessisbilityStateChangeIfNeeded 的判断不严谨问题 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

# 内存泄漏兜底
大量的内存泄漏,如果我们都靠推进研发解决,经常会出现生产大于消费的情况,针对这些未被消费的内存泄漏我们在客户端做了监控和止损,将 onDestory 的
Activity 添加到 WeakRerefrence 中,延迟 60s 监控是否回收,未回收则主动释放泄漏的 Activity 持有的 ViewTree
的背景图和 ImageView 图片 。
# 大对象
主要对三种类型的大对象进行优化
* 全局缓存:针对全局缓存我们按需释放和降级了不需要的缓存,尽量使用弱引用代替强引用关系,比如针对频繁泄漏的 EventBus 我们将内部的订阅者关系改为弱引用解决了大量的 EventBus 泄漏 。
* 系统大对象:系统大对象如 PreloadDrawable、JarFile 我们通过源码分析确定主动释放并不干扰原有逻辑,在启动完成或在内存触顶时主动反射释放 。
* 动画:用原生动画代替了内存占用较大的帧动画,并对 Lottie 动画泄漏做了手动释放 。

西瓜视频播放位置其他是哪里,西瓜视频的缓存文件在哪里

文章插图

图 14. 大对象优化点
# 小对象


以上关于本文的内容,仅作参考!温馨提示:如遇健康、疾病相关的问题,请您及时就医或请专业人士给予相关指导!

「四川龙网」www.sichuanlong.com小编还为您精选了以下内容,希望对您有所帮助: