【HarmonyOS NEXT】内存泄漏防护:常见场景与解决方案

一、背景

在鸿蒙开发中,内存泄漏会直接导致鸿蒙应用持续卡顿直至崩溃闪退,并且会因占用系统资源而拖慢整个设备、增加耗电。以下是我遇到的场景及解决示例,若有其他的场景欢迎补充

二、常见内存泄漏场景及解决方案

2.1、弹窗控制器未正确释放

在aboutToDisappear 中将弹窗引用置为 null

原因:CustomDialogController 可能持有组件上下文或其他资源引用,组件销毁时若不主动释放,可能导致这些资源无法被垃圾回收,造成内存泄漏。手动置为 null 可切断引用链,帮助释放资源。

示例代码:

@Entry
@Component
struct Index {
  // 在自定义组件即将析构销毁时将dialogController置空
  aboutToDisappear() {
    if (this.dialogController) {
      this.dialogController.close(); // ✅ 先确保弹窗关闭
      this.dialogController = null; // ✅ 再释放引用
    }
  }

  dialogController: CustomDialogController | null = new CustomDialogController({
    builder: CustomDialogExample({
      cancel: () => {
        console.info('Callback when the first button is clicked');
      },
      confirm: () => {
        console.info('Callback when the second button is clicked');
      }
    }),
    customStyle: true,
    alignment: DialogAlignment.Bottom,
    autoCancel: true,
    maskColor: '#808080',
  })

  build() {
    Column() {
      Button('click me')
        .onClick(() => {
          if (this.dialogController != null) {
            this.dialogController.open();
          }
        }).backgroundColor(0x317aff)
    }.width('100%').margin({ top: 5 })

  }
}

@CustomDialog
struct CustomDialogExample {
  controller?: CustomDialogController;
  cancel: () => void = () => {
  }
  confirm: () => void = () => {
  }

  build() {
    Column() {
      Text('可展示在主窗口外的弹窗')
        .fontSize(30)
        .height(100)
      Button('点我关闭弹窗')
        .onClick(() => {
          if (this.controller != undefined) {
            this.controller.close();
          }
        })
        .margin(20)
    }
  }
}

2.2、事件监听器未移除

在 aboutToDisappear 中调用 getContext().eventHub.off('xxx'),移除对全局事件总线的监听

作用:避免组件销毁后仍监听事件,导致事件总线持续持有组件引用,同时防止后续事件触发时执行已销毁组件的回调逻辑(可能引发错误或内存泄漏)

@Component
struct MyComponent {
  aboutToAppear() {
    getContext().eventHub.on('update', this.updateHandler);
  }

  aboutToDisappear() {
    getContext().eventHub.off('update'); // 关键:移除监听
  }

  private updateHandler = (data: any) => {
    // 处理逻辑
  }
}

2.3、 全局监听器未注销

在onWindowStageDestroy中释放全局事件监听和资源

取消网络状态监听:调用 NetworkStatus.unregister() 注销网络状态监听,避免网络模块长期持有能力上下文引用。

  onWindowStageDestroy(): void {
    //取消订阅默认网络状态变化的通知
    NetworkStatus.unregister()
    errorManager?.off('error', registerId, (err) => {
      if (err) {
        logger.error(`errorManager err: ${JSON.stringify(err)}`)
      }
    })
  }

2.4、页面共享数据未清理

页面销毁时清理共享存储数据

在 uiObserver 的 routerPageUpdate 回调中,针对页面 ABOUT_TO_DISAPPEAR 状态:
调用 AppStorage.delete(key) 清除当前页面在共享存储中缓存的所有数据(如 keyOfPageInfo、keyOfPageTime 等)。
目的:页面销毁后,其关联的业务数据(如页面信息、时间戳、曝光事件集合)不再占用内存,切断存储对页面数据的引用链。

  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index', (err) => {
      windowStage.getMainWindow((err: BusinessError, data)=>{
        let windowInfo: window.Window = data;
        if (!windowInfo?.getUIContext()) {
          return
        }
        uiObserver.on('routerPageUpdate',windowInfo.getUIContext(),(info: uiObserver.RouterPageInfo)=>{
          if (info.state == uiObserver.RouterPageState.ABOUT_TO_DISAPPEAR){
            // 关键清理操作 - 防止内存泄漏
            AppStorage.delete(keyOfPageInfo)        // 删除页面信息
            AppStorage.delete(keyOfPageTime)        // 删除时间信息
            AppStorage.delete(keyOfPageNewlyOpen)   // 删除页面状态
            AppStorage.delete(keyOfPageEventValues) // 删除事件参数
            AppStorage.delete(keyOfPageReportedExposeEvent) // 删除曝光记录
          }
        })
      })
      if (err.code) {
        hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
        return;
      }
      hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
    });
  }

2.5、定时器未清理

@Component
struct TimerComponent {
  private timerId: number = 0;

  aboutToAppear() {
    this.timerId = setInterval(() => {
      // 定时任务
    }, 1000);
  }

  aboutToDisappear() {
    // 必须清理定时器
    clearInterval(this.timerId);
  }
}

三、内存泄漏排查工具

- DevEco Studio性能分析器
- 内存快照对比
- 重复操作压力测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值