在Flutter鸿蒙应用调优过程中,遇到了黑屏,白屏,OOM 闪退和内存持续增长异常,该怎么排查?这四类问题总结成了一句口诀,大家可以细品:内存看 old 还是 new,黑屏看 REBUILT 还是 failed,引擎看 CREATE 和 DESTROY 配不配对,不渲染看 Vsync 句柄。怎么理解呢,大家接着往下看:

一、四类问题总览

问题看到的现象日志关键词通俗解释
Dart 内存超限应用卡顿、OOM 闪退Dart heap memory usage exceeds thresholdDart 代码用了太多内存,超过 1.5GB
GPU 上下文丢失黑屏、白屏、渲染异常GpuReclaim退后台时系统回收了 GPU 资源
引擎生命周期异常引擎创建、销毁异常FLUTTER_ENGINE_CREATE/DESTROY引擎没正常创建或被意外销毁
Vsync 管道故障不渲染、渲染卡顿vsync / Poll error垂直同步信号出问题了

二、Dart 内存超限

2.1 先理解机制

引擎每隔 10 秒检查一次 Dart 代码用了多少内存,如果超过 1.5GB 就会报警并记录日志:

参数说明
阈值1.5 GB超过就报警
检查间隔10 秒每 10 秒检查一次
触发次数单次报警一次后停止监控

注意:1.5GB 是引擎的检测阈值,系统级 OOM可能在更低的内存量时发生,因为系统是看的整体内存压力,不只是 Dart 的。

2.2 异常现象有哪些

看到的现象严重度
应用越来越卡
应用 OOM 闪退,被系统杀
Old Space 持续增长不回落
报警后不再监控
只有 HiLog 没有 HiAppEvent

2.3 三步进行排查

第 1 步,搜日志确认是否触发:

hdc shell hilog | grep -E "heap memory|threshold|FrameworkMemAnomaly"

典型日志:

W Flutter: Dart heap memory usage exceeds threshold:
total = 1600 MB (old = 1200 MB, new = 400 MB)
  • total = 1600 MB理解:总共用了 1600MB(超过 1.5GB 阈值了)

  • old = 1200 MB理解:Old Space(长期存活的对象)用了 1200MB

    • 如果 old 持续增长不回落 = 内存泄漏!
  • new = 400 MB理解:New Space(新创建的临时对象)用了 400MB

    • new 高但 GC 后回落 = 正常,只是短时间分配太多

第 2 步,按内存分布判断问题类型:

内存分布诊断怎么办
old 高且持续增长内存泄漏检查 dispose() 是否正确释放资源
new 高但 GC 后降短时间大量分配优化批量操作,减少临时对象
old 和 new 都高综合问题先治泄漏,再优化分配

第 3 步,用 DevTools Memory 精确定位

2.4 四个常见原因与修复

原因 1:图片缓存太多

现象:加载很多图片后内存涨,页面退出后不回落。

// 检查图片缓存使用量
print(PaintingBinding.instance.imageCache.currentSize);       
// 缓存了多少张
print(PaintingBinding.instance.imageCache.currentSizeBytes); 
// 缓存占多少字节
// 限制缓存大小:100 张 / 50MB(默认 1000 张 / 100MB)
PaintingBinding.instance.imageCache.maximumSize = 100;
PaintingBinding.instance.imageCache.maximumSizeBytes = 50 * 1024 * 1024;
// 大图按显示尺寸解码,不解码全尺寸
Image.network(url,
  cacheWidth: (screenWidth * devicePixelRatio).toInt(),
  cacheHeight: (screenHeight * devicePixelRatio).toInt(),
);

原因 2:大列表没有懒加载

现象:长列表页面内存暴涨。

// 错误:一次性构建所有子项,1000 个全构建
ListView(children: items.map((e) => ItemWidget(e)).toList());
// 正确:懒加载,只构建可见的;更好:加 itemExtent 免测量
ListView.builder(
  itemExtent: 80.0,
  itemCount: items.length,
  itemBuilder: (context, index) => ItemWidget(items[index]),
);

原因 3:资源没释放,内存泄漏

现象:页面退出后内存不回落,反复进出持续增长。

class _MyPageState extends State<MyPage> {
  late StreamSubscription _sub;
  late AnimationController _controller;
  late TextEditingController _textController;
  @override
  void dispose() {
    
// 必须释放,否则就是内存泄漏
    _sub.cancel();            
// 取消 Stream 订阅
    _controller.dispose();      
// 释放动画控制器
    _textController.dispose(); 
// 释放文本控制器
    super.dispose();
  }
}

原因 4:Isolate 没关闭

现象:用了 Isolate.spawn 后内存不释放。

Isolate? isolate;
Future<void> startWork() async {
  final receivePort = ReceivePort();
  isolate = await Isolate.spawn(_entryPoint, receivePort.sendPort);
}
void stopWork() {
  isolate?.kill(priority: Isolate.immediate);  
// 用完后关闭
  isolate = null;
}
// 提示:compute() 自动管理 Isolate 生命周期,优先使用
final result = await compute(_heavyTask, data);

三、GPU 上下文丢失

3.1 先理解机制

应用退到后台或系统内存不足时,鸿蒙系统会回收 GPU 资源。退后台时引擎会自动处理清理 GPU 资源,回前台时重建 GPU 上下文。在重建期间如果丢失了上下文,屏幕就会持续黑屏。

3.2 现象

看到的现象严重度说明
退后台再回前台,短暂黑屏后恢复正常GPU 回收后成功恢复
回前台后持续黑屏、白屏Surface 重建失败了
回前台后画面卡顿几秒重建耗时较长
图片显示异常图片缓存被清理,需要重新加载
退后台后视频、纹理不更新正常帧闸门开启,正常行为
反复黑屏闪烁生命周期状态抖动,频繁切换前后台

3.3 排查方法

搜 GpuReclaim,按时间线比对日志。一段正常的日志流水如下:

[T+0]   GpuReclaim: Lifecycle Resumed -> Paused        
← 退后台了
[T+0]   GpuReclaim: Restore -> Aggressive           
← 决定:回收 GPU
[T+0]   GpuReclaim: ExecuteAggressive - deferring 1000ms  
← 延迟 1 秒执行,等 PiP 检测
[T+0]   GpuReclaim: Sending memoryPressure        
← 通知 Dart 清理图片缓存
[T+0]   GpuReclaim: [Raster] Surface torn down      
← GPU 资源已释放
[T+N]   GpuReclaim: Lifecycle Paused -> Resumed      
← 回前台了
[T+N]   GpuReclaim: Aggressive -> Restore            
← 决定:恢复
[T+N]   GpuReclaim: Rebuilding onscreen context     
← 正在重建
[T+N]   GpuReclaim: [Raster] Surface REBUILT         
← 重建成功,画面恢复

看到 Surface REBUILT 就是成功,如果看到 SetDisplayWindow failed 就是失败了,确认失败后,就要具体再深度分析了。

3.4 三个常见问题与修复

问题 1:回前台后持续黑屏

排查:搜 SetDisplayWindow failed during rebuild。

三种原因

(1)Surface 重建失败:检查 native_window 是否还有效

(2)重建未执行:检查 ExecuteRestore 日志是否存在

(3)应用层 onDestroy 被过早调用:确保 FlutterPage.onDestroy 在真正销毁时才调用

// FlutterPage.ets:确保 onLoad / onDestroy 正确配对
.onLoad((context) => {
  this.flutterView = FlutterManager.getInstance().getFlutterView(this.viewId);
  this.surfaceLifecycleToken = this.flutterView?.onSurfaceCreated() ?? 0;
})
.onDestroy(() => {
  
// 必须在真正销毁时才调用,不能提前
  this.flutterView?.onSurfaceDestroyed(this.surfaceLifecycleToken);
  this.flutterView = null;
})

问题 2:退后台后纹理、视频还在耗电

排查:搜 frame gate enabled。

两种原因:

(1)生命周期没正确传到引擎:确保 onPageShow / onPageHide 被正确调用(2)插件没实现暂停逻辑:插件实现 onInactive() / onActive()。

应用层要转发页面可见性:

// FlutterAbilityAndEntryDelegate.ets 必须转发页面可见性
onPageShow() {
  this.flutterView?.onPageVisibilityChanged(true);
}
onPageHide() {
  this.flutterView?.onPageVisibilityChanged(false);
}

问题 3:图片显示异常

原因:GPU 回收时图片缓存被清理了(Sending memoryPressure),重新显示时需要重新解码。

修法方法:回前台后预加载关键图片:

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
  if (state == AppLifecycleState.resumed) {
    precacheImage(AssetImage('assets/key_image.png'), context);
  }
}

3.5 关键日志速查

日志通俗解释正常吗
Restore -> Aggressive开始回收 GPU正常,退后台时
Aggressive -> Restore开始恢复 GPU正常,回前台时
Surface torn downGPU 资源已释放正常
Surface REBUILT重建成功正常
SetDisplayWindow failed重建失败异常
frame gate enabled帧闸门开启正常,退后台时
Sending memoryPressure通知清理图片缓存正常
Deferred aggressive cancelled - PiP visiblePiP 可见,取消回收正常,PiP 场景

四、引擎生命周期

关键两个事件:FLUTTER_ENGINE_CREATE 和 FLUTTER_ENGINE_DESTROY

现象:

看到的现象严重度说明
应用启动后白屏引擎没创建成功
应用闪退,无 Dart 异常引擎意外销毁
内存持续增长引擎重复创建但没销毁
内存监控不工作引擎创建时没启动监控

排查:搜两个事件名,检查 CREATE 和 DESTROY 是否 1:1 配对。

如果CREATE 多于 DESTROY,说明有引擎没被销毁,意味着内存泄漏了:

hdc shell hilog | grep -E "FLUTTER_ENGINE_CREATE|FLUTTER_ENGINE_DESTROY"

问题 1:引擎创建失败,白屏

三个检查点:

(1)Flutter 配置错误:检查 flutter_ohos.json5

(2)Dart bundle 加载失败:检查 flutter_assets 是否完整打包

(3)SO 库加载失败:检查 libflutter.so 是否正确打包。

问题 2:引擎重复创建,内存泄漏。每次都 new 一个新引擎,修法是复用:

// 错误:每次都创建新引擎
getEngine(): FlutterEngine {
  return new FlutterEngine(context);
}
// 正确:复用引擎
private flutterEngine: FlutterEngine | null = null;
getEngine(): FlutterEngine {
  if (this.flutterEngine === null) {
    this.flutterEngine = new FlutterEngine(context);
  }
  return this.flutterEngine;
}

五、Vsync 管道故障

看到的现象严重度对应日志
完全不渲染,画面静止致命vsync_handle_ is nullptr
偶发卡顿AwaitVSync…failed
帧率无法设置,120fps 无效SetExpectedFrameRateRange failed
不渲染加卡死致命Failed to dlopen libnative_vsync.so

排查命令和日志含义:

hdc shell hilog | grep -E "vsync|Vsync|VSync|Poll error"
日志通俗解释严重度
vsync_handle_ is nullptr节拍器没装上,句柄无效致命
AwaitVSync…failed:节拍器不响了,请求失败致命
Failed to dlopen libnative_vsync.soVsync 库加载失败致命
SetExpectedFrameRateRange failed帧率设置失败
Poll error:消息循环出错

三种故障的修法:

(1)完全不渲染:排查Vsync 库是不是没加载

(2)偶发卡顿:排查下是不是系统繁忙导致请求偶尔失败

(3)120fps 无效:排查下是不是设备或系统不支持


本次内容比较多,相关排查方法希望能帮到大家~ 同时大家也持续关注,后续会持续更新

小伙伴们记得点赞+关注

关注 CPF-Flutter 社区

“AI再牛,技术不能丢”

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐