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

前言

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

上一期的文章讲述了 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等资源的泄露,实际上每个直播间可能泄露的更多。

相关推荐
轻口味24 分钟前
HarmonyOS 7 新特性3:TiledGSNode——轻带看让 71MB 庭院按视口按需加载:真机实测与零请求降级
华为·harmonyos·鸿蒙·tiledgsnode
李游Leo1 小时前
《HarmonyOS 7 ArkGraphics 3D 空间设计开发实战》01:从空场景到第一个可运行的3D房间【鸿蒙心迹】
3d·华为·harmonyos
猛犸象限1 小时前
【共创稿事节】关卡进了源码,却对不上 JSON——回归不是再截一张图
harmonyos
用户29469405448161 小时前
鸿蒙真机调试三板斧:Playwright 为什么驱动不了 Electron-on-鸿蒙
harmonyos·deepseek
木子雨廷2 小时前
第 26 天|Worker 多线程:重任务不阻塞 UI
harmonyos
2501_919749032 小时前
华为鸿蒙免费主题壁纸工具—小羊免费壁纸
华为·harmonyos·鸿蒙
梦想不只是梦与想3 小时前
华为AGC:应用留存分析
harmonyos·用户分析·行为分析·下载安装·用户留存分析
三掌柜6663 小时前
ArkWeb 手记 05|H5 文件上传怎么接
harmonyos
李洋-蛟龙腾飞公司3 小时前
HarmonyOS Dev Assistant安装基本流程与使用(一)
华为·harmonyos·harmonyos next
Sunny_GMF3 小时前
所见即所得编辑器原理实战:源码与渲染永不失真的三层一致性设计(Markdown/CodeMirror 装饰)
microsoft·编辑器·ai编程·harmonyos