鸿蒙抖音直播最严重的一个内存泄漏分析与解决

前言

在我接手鸿蒙稳定性之前,直播的预览流跟直播间都有严重的内存泄漏,首先就算是不进入直播间,只要刷到一次直播预览流,就会泄露一些内存。其次进入直播间的泄露更加的严重,直播间内滑动个几十次,必现闪退。

上一期的文章讲述了 NodeController 循环依赖导致的内存泄露,解决了直播预览流持续泄露的问题 (预览流每刷到一个直播间就会泄露一些内存),感兴趣的可以点开下面的文章看看。juejin.cn/post/752946...

最近做了内存泄漏的二期优化,直播间内的泄露也大幅度缓解,刷上几百个也不会出现闪退问题,这期文章重点介绍下其中的一个优化项,鸿蒙 XComponent 导致的严重内存泄漏,平均每个直播间泄露30mb+。接下来讲一下XComponent 优化的启动背景以及优化思路。

背景

线上每天都有用户反馈直播间刷上几十个就会发生闪退,捞取日志都是内存不足被系统强杀,分析 js 内存快照未发现有特别严重的内存泄露。

后续华为厂商也找了我们好几次,每滑动一个直播间,内存水位就会上涨一大截,多刷几个直播间就会被强杀(这个问题我们也是已知的,修复了很多JS层内存泄漏,依然没有缓解)。

后来经过厂商内部分析,初步判断发生在XComponent 的内存泄露。

问题排查思路

根据厂商提供的信息,于是我将直播间能找到的XComponent都设置了可识别id(加了点特殊前缀),出了一个特殊的包发给了厂商。最终定位到是礼物的xComponent,并且一个直播间会有两个播放器,所以一个直播间至少会泄露27mb左右。这里有个小插曲,由于代码有误,将 XComponent 的id设置成了一个固定的值,由于XComponent存在内存泄漏,所以多刷几个直播间就会发生崩溃。所以再设置 XComponent id 的时候,一定要确保它是唯一的。

虽然就只有三个实例,根据上面的13926400基本上可以确定就是礼物播放器导致的。

于是分析礼物 XCompnent 的内存快照,发现在JS层,没有任何的内存泄露,故这个泄露大概率发生在Native层,

XComponent 拥有一块独立的缓存去上屏,这块缓存需要手动释放,

代码分析

根据华为官方的提示,业务方大概是误用了developer.huawei.com/consumer/cn... 这里的方法,我根据 OH_NativeWindow 关键字进行搜索,终于发现了可疑的用法。

经过分析,在礼物的代码里发现了下面的用法。从代码上看,貌似nativeWindow_的引用跟解引用都是成对的。

跟相关同学沟通后,他觉得这里进行了成对调用,不会有问题。

scss 复制代码
// 对象在析构的时候会传一个 newWindow = nullptr 用来回收nativeWindow_
if (nativeWindow_ != nullptr) {
    (void)OH_NativeWindow_NativeObjectUnreference(nativeWindow_);
}
nativeWindow_ = newWindow;
if (nativeWindow_ != nullptr) {
    (void)OH_NativeWindow_NativeObjectReference(nativeWindow_);

本着试一试的想法,加日志后发现OH_NativeWindow_NativeObjectUnreference(nativeWindow_) 不会被调用,这下几乎可以石锤是这个地方的问题了。

深入分析之后,发现这里的业务逻辑错误也是非常的有意思,简单的捋一下如下:

1 release操作被post到一个循环方法里执行

2 唤醒等待锁,接着将running_置成了false。

3 唤醒锁后开始执行release操作,

4 而release中的OH_NativeWindow_NativeObjectUnreference 操作又被post了一下,但是running_已经被置成了false,

5 OH_NativeWindow_NativeObjectUnreference的post方法被拦截。

核心代码如下(已脱敏)

post逻辑

arduino 复制代码
void obj::post(const Task &task) {
    if (!running_) {
        ...
        return;
    }

    {
        ...
        tasks_.push_back(task);
    }

    if (std::this_thread::get_id() != threadId_) {
        ...
        wakeUpCond_.notify_one();
    }
}

循环逻辑:

arduino 复制代码
while (running_) {
    {
        ...
        wakeUpCond_.wait();
        ...
    }

    std::vector<Task> tasks;
    {
        // ...
        tasks.swap(tasks_);
    }
    
    
    for (const auto &task : tasks) {
        task(*renderContext_);
    }
    ...
}

释放逻辑

scss 复制代码
OBj::~OBj() noexcept {
   
    post( {
        ...
        release()
        ...
    });
    
    running_ = false;
}

void release() {
    ...
    post({
        OH_NativeWindow_NativeObjectUnreference(nativeWindow_)
    })
    ...
}

修复效果

找到问题之后,将post逻辑优化即可,最终修复效果也是非常的好,虽然还是有一些小的内存泄漏,但是内存增长已经很缓慢了,连续刷上百个直播间也不会崩溃了。

这里除了NativeWindow的缓存未释放以外,还有一些shader资源、gl等资源的泄露,实际上每个直播间可能泄露的更多。

相关推荐
2501_9445264217 分钟前
Flutter for OpenHarmony 万能游戏库App实战 - 知识问答游戏实现
android·开发语言·javascript·python·flutter·游戏·harmonyos
2501_944424121 小时前
Flutter for OpenHarmony游戏集合App实战之数字拼图滑动交换
android·开发语言·flutter·游戏·harmonyos
不会写代码0001 小时前
Flutter 框架跨平台鸿蒙开发 - 手写笔记工具开发教程
笔记·flutter·华为·harmonyos
小风呼呼吹儿2 小时前
Flutter 框架跨平台鸿蒙开发 - 老年便捷助手:打造适合老年人的智能工具箱
flutter·华为·harmonyos
lbb 小魔仙2 小时前
【Harmonyos】开源鸿蒙跨平台训练营DAY3:为开源鸿蒙跨平台工程集成网络请求能力,实现数据清单列表的完整构建与开源鸿蒙设备运行验证(及常见问题与方法)
windows·开源·harmonyos·鸿蒙·开源鸿蒙·鸿蒙开平台应用
奔跑的露西ly2 小时前
【HarmonyOS NEXT】Stage模型与FA模型对比
华为·harmonyos·鸿蒙
小雨下雨的雨2 小时前
禅息:在鸿蒙与 Flutter 之间寻找呼吸的艺术
算法·flutter·华为·重构·交互·harmonyos
[H*]2 小时前
Flutter框架跨平台鸿蒙开发——Image综合应用
flutter·华为·harmonyos
小风呼呼吹儿2 小时前
Flutter 框架跨平台鸿蒙开发 - 倒计时秒表:打造多功能计时工具
网络·flutter·华为·harmonyos
AI_零食2 小时前
鸿蒙跨端框架 Flutter 学习 Day 4:异步编程基础——Future 与非阻塞执行的物理真相
学习·flutter·harmonyos