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

前言

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

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

相关推荐
安卓开发者8 小时前
鸿蒙NEXT应用接入快捷栏:一键直达,提升用户体验
java·harmonyos·ux
HMS Core8 小时前
消息推送策略:如何在营销与用户体验间找到最佳平衡点
华为·harmonyos·ux
Brianna Home9 小时前
【案例实战】鸿蒙分布式调度:跨设备协同实战
华为·wpf·harmonyos
Bert丶seven9 小时前
鸿蒙Harmony实战开发教学(No.4)-RichText组件基础到高阶介绍篇
华为·harmonyos·arkts·鸿蒙·鸿蒙系统·arkui·开发教程
鸿蒙小白龙10 小时前
openharmony之分布式蓝牙实现多功能场景设备协同实战
分布式·harmonyos·鸿蒙·鸿蒙系统·open harmony
爱吃水蜜桃的奥特曼11 小时前
玩Android Harmony next版,通过项目了解harmony项目快速搭建开发
android·harmonyos
鸿蒙小白龙11 小时前
openharmony之分布式购物车开发实战
分布式·harmonyos·鸿蒙·鸿蒙系统·open harmony
鸿蒙小白龙12 小时前
openharmony之分布式相机开发:预览\拍照\编辑\同步\删除\分享教程
分布式·harmonyos·鸿蒙·鸿蒙系统·open harmony
安卓开发者14 小时前
鸿蒙NEXT鼠标光标开发完全指南
华为·计算机外设·harmonyos
●VON20 小时前
重生之我在大学自学鸿蒙开发第九天-《分布式流转》
学习·华为·云原生·harmonyos·鸿蒙