Flutter:从 Dart(通过 ffi)异步调用本机 C 代码导致内存泄漏

Flutter: Async call of native C code from Dart (via ffi) cause memory leak

随机出现我的 Flutter 应用程序的空白屏幕,显然是内存不足错误。请参阅下面的 DEBUG CONSOLE 日志。

这是我在崩溃期间的 Dart DevTools 内存视图 - 正如您所看到的,内存消耗在整个会话期间保持稳定:

我想知道是否还有其他内存类型需要检查?

顺便说一句,应用程序实际上并没有崩溃,而是显示空白(背景色)屏幕。它以某种方式对水龙头做出反应——因为我可以从我的一些小部件中看到我的调试打印。它对点击有反应但不显示任何内容 - 屏幕是空的。

应用程序处于显示空屏状态时的 DEBUG CONSOLE 日志:

libc    ( 8725): pthread_create failed: couldn't mprotect R+W 1028096-byte thread mapping region: Out of memory
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
W/Adreno-GSL( 8725): <sharedmem_gpuobj_alloc:2706>: sharedmem_gpumem_alloc: mmap failed errno 12 Out of memory
E/Adreno-GSL( 8725): <gsl_memory_alloc_pure:2297>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
...
...
I/Surface ( 8725): opservice is null false

flutter doctor -v

[√] Flutter (Channel stable, 1.22.5, on Microsoft Windows [Version 10.0.18363.1256], locale en-US)
    • Flutter version 1.22.5 at C:\sdk\flutter
    • Framework revision 7891006299 (5 weeks ago), 2020-12-10 11:54:40 -0800
    • Engine revision ae90085a84
    • Dart version 2.10.4

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
    • Android SDK at C:\Users\esikerin\AppData\Local\Android\sdk
    • Platform android-30, build-tools 30.0.2
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[!] Android Studio (version 4.0)
    • Android Studio at C:\Program Files\Android\Android Studio
    X Flutter plugin not installed; this adds Flutter specific functionality.
    X Dart plugin not installed; this adds Dart specific functionality.
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] VS Code (version 1.52.1)
    • VS Code at C:\Users\esikerin\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.18.1

[√] Connected device (1 available)
    • GM1900 (mobile) • ff184dad • android-arm64 • Android 10 (API 29)

更新:

提供更多背景信息。在我以下列方式从我的 Dart 代码异步实现 运行ning 本机 C 代码(通过 ffi)后,我开始遇到这个问题。

我在 Dart 中创建了 Uint8 列表并将其传递给我的 C 代码。 C 代码将启动多个并行线程和 return 到 Dart。列表的长度等于线程数。然后我定期检查 Dart 代码中的列表以了解所有 C 线程何时完成。显然,C 线程在完成时会将相应的列表元素设置为 1。这样我就可以等待并行 C 线程从 Dart 代码完成,而不会阻塞我的主 UI 线程。顺便说一句,当我等待 C 代码完成时,这个等待循环只是 运行ning - 所以在应用程序的整个生命周期中我不会 运行 它。

  var threadStatusesPtr = xffi.allocate<ffi.Uint8>(count: _numThreads);
  var threadStatuses = threadStatusesPtr.asTypedList(_numThreads);
  for (int i=0; i<threadStatuses.length; i++) {
    threadStatuses[i] = 0;
  }

  // run C-function that starts parallel threads and returns
  // each C-thread when finished writes 1 to corresponding threadStatuses array element
  // so when all C-threads finished all elements in threadStatuses list should be non-zero
  _applyFiltersNative(_numThreads, threadStatusesPtr, ...);

  // waiting for all C-threads to finish
  while (threadStatuses.any((status) => status == 0)) {
    await Future.delayed(Duration(milliseconds: 10));
  }

我想知道是否存在任何潜在问题,为什么我不应该从正在并行 C 线程中修改的 Dart 代码访问相同的列表。

P.S.: 以前当我 运行 通过等待所有线程完成 (pthread_join) 同步我的 C 代码时 returning to Dart 我从未遇到过这个问题。

我找到了问题的根本原因。

默认情况下 pthread_create() 创建可连接的线程。这意味着您必须使用 pthread_join() 加入他们 - 否则他们将永远不会释放分配的资源。这正是我的情况 - 完成的线程从未释放分配的内存,因此一段时间后我遇到了“内存不足”问题。这就是为什么我从未在同步调用中看到过这个问题——因为我用 join 等待所有线程。

如果你想从 Dart 代码异步调用你的 C-func 你不想用 pthead_join() 等待你的线程 - 你想创建线程和 return飞镖。在这种情况下,您必须创建分离的线程 - 即在 pthread_create() 之后调用 pthread_detach() - 这样您的线程在完成后将不会保留任何资源。

唯一剩下的问题是:为什么我在 Dart DevTools 中看不到任何内存泄漏?如果我看到内存泄漏,我可能会更早地解决这个问题,然后再将任何内容发布到 Stack Overflow :)