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 :)
随机出现我的 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 :)