08、Flutter FFI 内存管理
Flutter FFI 学习笔记系列
- 《Flutter FFI 最简示例》
- 《Flutter FFI 基础数据类型》
- 《Flutter FFI 函数》
- 《Flutter FFI 字符串》
- 《Flutter FFI 结构体》
- 《Flutter FFI 类》
- 《Flutter FFI 数组》
- 《Flutter FFI 内存管理》
- 《Flutter FFI Dart Native API》
在前面的章节中,介绍了基础数据类型、字符串、结构体、类、数组等知识点,接下来将介绍一下 FFI 中的内存管理。
在C语言开发过程,内存的申请和回收都是由开发者自己负责的,前面的很多文章都有演示到内存的分配和回收,今天继续来深入学习一下。
1、内存管理介绍
Dart FFI 提供了一些 API,可以让开发者使用 Dart 代码在 Native 中申请内存和释放内存。这些 API 包括 Allocator
、_MallocAllocator
、_CallocAllocator
等。
Allocator
是抽象类,_MallocAllocator
、_CallocAllocator
是它的两个实现类。
Allocator
有两个方法:allocate()
和 free()
,分别用于申请内存和释放内存。Allocator
类的代码如下:
/// Manages memory on the native heap.
abstract class Allocator {/// Allocates [byteCount] bytes of memory on the native heap.////// If [alignment] is provided, the allocated memory will be at least aligned/// to [alignment] bytes.////// Throws an [ArgumentError] if the number of bytes or alignment cannot be/// satisfied.Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment});/// Releases memory allocated on the native heap.////// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be/// freed.void free(Pointer pointer);
}/// Extension on [Allocator] to provide allocation with [NativeType].
extension AllocatorAlloc on Allocator {/// Allocates `sizeOf<T>() * count` bytes of memory using/// `allocator.allocate`.////// This extension method must be invoked with a compile-time constant [T].external Pointer<T> call<T extends NativeType>([int count = 1]);
}
代码说明:
allocate()
:用于申请内存,参数byteCount
表示需要申请的内存的字节数,该函数返回指向该内存的指针,该内存是由 Native 进行分配的;free()
:释放指针所指向的内存。call()
:这是Allocator
的一个扩展函数,让申请内存的写法更简单。
_MallocAllocator
与 _CallocAllocator
都是 Allocator
的子类,区别在于:
_MallocAllocator
申请内存的时候,调用了 C 语言的malloc()
;_CallocAllocator
申请内存的时候,调用了 C 语言的calloc()
;malloc
和calloc
的一个重大区别是:calloc
会自动把内存初始化为0
;
在 Dart 中,已经定义了这两个类的实例,而且是全局的,可以任意调用:
/// Manages memory on the native heap.
///
/// Does not initialize newly allocated memory to zero. Use [calloc] for
/// zero-initialized memory allocation.
///
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
/// `HeapAlloc` and `HeapFree` against the default public heap.
const Allocator malloc = _MallocAllocator();/// Manages memory on the native heap.
///
/// Initializes newly allocated memory to zero. Use [malloc] for uninitialized
/// memory allocation.
///
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
/// public heap.
const Allocator calloc = _CallocAllocator();
代码说明:
- 上面的
malloc
和calloc
是两个实例,可以在任意地方调用;
2、内存分配与释放
前面介绍了使用 Allocator 类进行内存的申请和释放,现在来介绍如何使用 malloc
和 calloc
这两个对象来分配内存、释放内存。
2.1 内存分配与释放
下面的示例中,演示了如何使用 malloc
和 calloc
来申请内存和释放内存:
int size = sizeOf<Int32>();
Pointer<Int32> a = malloc.allocate(size);
Pointer<Int32> b = calloc.allocate(size);
Pointer<Int32> c = calloc.call();print("a=${a.value}, b=${b.value}, c=${c.value}, sizeof<Int32>=$size");a.value = 30;
b.value = 27;
c.value = 60;print("a=${a.value}, b=${b.value}, c=${c.value}");malloc.free(a);
calloc.free(b);
calloc.free(c);// 输出结果:
// I/flutter (11797): a = 82, b = 0, sizeof<Int32> = 4
// I/flutter (11797): a = 30, b = 27
代码说明:
- 这里,我们不再需要使用
DynamicLibrary
来加载库,因为malloc
与calloc
的内部已经帮我们做了一这步了; - 调用
allocate()
函数时,需要明确指定byteCount
,这里我们通过sizeOf()
函数来获取Int32
的字节数; - 调用
call()
函数时,不需要指定byteCount
,call()
函数内部已经帮我们调用了sizeOf()
函数了; - 从上面的示例可以看出,
calloc
申请内存之后会初始化为0
,而malloc
则不会; malloc
与calloc
两者的free()
函数的实现都一样。
2.2 数组内存分配与释放
下面的示例中,演示如何通过calloc
来创建数组:
int size = sizeOf<Int32>();Pointer<Int32> a = malloc.allocate(10 * size);
Pointer<Int32> b = calloc.call(10);print("a = ${a.asTypedList(10)}");
print("b = ${b.asTypedList(10)}");for (int i = 0; i < 10; i++) {a[i] = 10 * i + i;b[i] = 100 * i + i;
}print("a = ${a.asTypedList(10)}");
print("b = ${b.asTypedList(10)}");malloc.free(a);
calloc.free(b);// 输出结果:
// I/flutter (12223): a = [-1574300648, 111, 243933264, 113, -1637386232, 111, -1637385960, 111, 1049256144, 112]
// I/flutter (12223): b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
// I/flutter (12223): a = [0, 11, 22, 33, 44, 55, 66, 77, 88, 99]
// I/flutter (12223): b = [0, 101, 202, 303, 404, 505, 606, 707, 808, 909]
代码说明:
- 上述示例中,再一次证明了,
calloc
会把内存初始为0
,而malloc
不会; - 数组指针可以转为 Dart
List
,这样就可以使用 List 的各种便捷方法:foreach
,where
等;
2.3 结构体内存分配与释放
前面的章节中,介绍了如何在把 C 中的结构映射到 Dart 来使用。
下面的示例,演示如何不使用 C 语言,完全在 Dart 定义结构体、创建结构体、销毁结构体。
//定义一个结构体,表示2D平面上的一点
class Point extends Struct {@Int32()external int x;@Int32()external int y;String toDebugString() => "{x=$x, y=$y}";
}void test() {//获取Point所占内存大小int size = sizeOf<Point>();//创建结构体Pointer<Point> p1 = calloc.call();Pointer<Point> p2 = calloc.call();print("size of point is $size");print("p1 = ${p1.ref.toDebugString()}");print("p2 = ${p2.ref.toDebugString()}");p1.ref.x = 10;p1.ref.y = 20;p2.ref.x = 300;p2.ref.y = 400;print("p1 = ${p1.ref.toDebugString()}");print("p2 = ${p2.ref.toDebugString()}");//销毁结构体calloc.free(p1);calloc.free(p2);
}// 输出结果:
// I/flutter (12223): size of point is 8
// I/flutter (12223): p1 = {x=0, y=0}
// I/flutter (12223): p2 = {x=0, y=0}
// I/flutter (12223): p1 = {x=10, y=20}
// I/flutter (12223): p2 = {x=300, y=400}
代码说明:
- 无
3、自动释放池 —— Arena
有时候需要临时申请内存做一些操作,操作完了就把内存释放掉,但是往往忘记释放内存,又或者 free(a)
写错为 free(b)
,引起内存泄漏。
其实 Dart 已经为我们实现了一个自动释放池,可以应对上述使用场景。
Arena
是 Allocator
的另一个实现类,它与 _MallocAllocator
、_CallocAllocator
最大的不同是:它自动释放由它分配的内存。
Arena
内部默认使用 calloc
来申请内存,每次申请内存的时候,它都会记录下来,后面可以调用它的 releaseAll()
方法全部释放掉。
Arena
的核心代码如下:
class Arena implements Allocator {//持有一个Allocator,用于实际的内存分配和回收final Allocator _wrappedAllocator;//这个List用于记录已分配的内存的指针final List<Pointer<NativeType>> _managedMemoryPointers = [];//构造函数,默认使用 callocArena([Allocator allocator = calloc]) : _wrappedAllocator = allocator;//分配内存Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {//确保当前对象处于使用状态_ensureInUse();//启用_wrappedAllocator申请内存final p = _wrappedAllocator.allocate<T>(byteCount, alignment: alignment);//记录已申请的内存_managedMemoryPointers.add(p);return p;}//这个是空函数,如果需要释放内存,应调用releaseAll@overridevoid free(Pointer<NativeType> pointer) {}//释放所有内存void releaseAll({bool reuse = false}) {//释放后,当前对象是否还能使用if (!reuse) {_inUse = false;}//释放内存for (final p in _managedMemoryPointers) {_wrappedAllocator.free(p);}_managedMemoryPointers.clear();}
}/// 该方法自动实现了 Arena 的创建和销毁,使用更便捷
R using<R>(R Function(Arena) computation,[Allocator wrappedAllocator = calloc]) {final arena = Arena(wrappedAllocator);bool isAsync = false;try {final result = computation(arena);if (result is Future) {isAsync = true;return (result.whenComplete(arena.releaseAll) as R);}return result;} finally {if (!isAsync) {arena.releaseAll();}}
}R withZoneArena<R>(R Function() computation,[Allocator wrappedAllocator = calloc]) {final arena = Arena(wrappedAllocator);var arenaHolder = [arena];bool isAsync = false;try {return runZoned(() {final result = computation();if (result is Future) {isAsync = true;return result.whenComplete(() {arena.releaseAll();}) as R;}return result;}, zoneValues: {#_arena: arenaHolder});} finally {if (!isAsync) {arena.releaseAll();arenaHolder.clear();}}
}
代码说明:
free()
函数是空函数,如果需要释放内存,应调用releaseAll()
。using()
函数自动实现了Arena
的创建和销毁,使用更便捷,同样还有withZoneArena()
方法。
下面的示例,演示了如何使用 Arena
完成内存的自动释放。
//创建Arena
Arena arena = Arena();//使用Arena分配内存
int length = 5;
Pointer<Int32> array = arena.call(length);
Int32List list = array.asTypedList(length);print("before array=$list");for (int i = 0; i < length; i++) {list[i] = i * 100 + i * 5;
}print("after array=$list");//回收内存
arena.releaseAll();// 输出结果:
// I/flutter (12223): before array=[0, 0, 0, 0, 0]
// I/flutter (12223): after array=[0, 105, 210, 315, 420]
代码说明:
- 调用
asTypedList()
之后,并不是创建了一个List
,它的数据还是存储在Pointer<>
所指向的内存;
上面的代码也可以这样写:
using((arena) {int length = 5;Pointer<Int32> array = arena.call(length);Int32List list = array.asTypedList(length);print("before array=$list");for (int i = 0; i < length; i++) {list[i] = i * 100 + i * 5;}print("after array=$list");
});
代码说明:
- 上述写法:省去了
Arena
的创建,以及releaseAll
的调用,执行结果是一样的
4、总结
上面介绍了 FFI 的内存管理知识,加上前面章节的知识点,已经可以应付很多开发需求了。如果需要更高级的用法,则可能需要使用 Dart Native API 来解决了,后面的章节中,将会介绍如何使用 Dart Native API 实现 C 异步回调 Dart 等高级用法,欢迎关注。
08、Flutter FFI 内存管理相关推荐
- 01、Flutter FFI 最简示例
Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...
- 03、Flutter FFI 函数
Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...
- Dart 内存管理机制
Dart汇总请点击这里 文章目录 Dart内存管理 Dart运行环境(VM) 新生代(New Generation) 老年代(Old Generation) 内存管理算法 iOS端 Android端 ...
- [objective-c] 08 - 内存管理
OC语言中的内存管理机制为ARC(Automatic Reference Counting,自动引用计数).于2011年中旬推出,替换陈旧且低效的手动内存管理,关于手动内存管理的内容,本章教程不再讲授 ...
- 一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc
大家好,我是无际. 有一周没水文了,俗话说夜路走多了难免遇到鬼. 最近就被一个热心网友喷了. 说我的文章没啥营养,所以今天来一篇烧脑的. 哈哈,开个玩笑,不要脸就没人能把我绑架. 主要是最近研发第二代 ...
- Objective-C 内存管理之ARC规则
基本概念 ARC为自动引用计数,引用计数式内存管理的本质并没有改变,ARC只是自动处理"引用计数"的相关部分. 在编译上,可以设置ARC有效或无效.默认工程中ARC有效,若设置无效 ...
- python内存管理错误的是_关于Python内存管理,下列说法错误的是
53 Python中变量可以不指定类型,会自动根据赋值语句来决定类型 同时,使用前必须赋值,在赋值的同时变量也就创建了 发表于 2018-02-28 22:50:02 回复(0) 61 本题答案选 B ...
- (0048)iOS开发之内存管理探究
注意网上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得 retainCount都是不准确的,特别是在对象拷贝操作之后你会发现 ...
- linux 内存管理 page fault带来的性能问题
Linux进程如何访问内存 Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源. 原因后面会讲到. 为什么需要虚拟内存地址空间 假设某个进程需要4MB的空间,内存假 ...
- [内存管理] linux kernel内存映射实例分析
作者:JHJ(jianghuijun211@gmail.com ) 日期:2012/08/24 欢迎转载,请注明出处 引子 现在android智能手机市场异常火热,硬件升级非常迅猛,arm corte ...
最新文章
- java threadgourp_Java Thread getThreadGroup()方法
- etcd集群部署与遇到的坑
- 启明云端分享|ESP32-S3一颗融合AI和IOT于一体的芯片,到底有哪些功能!能应用在哪些场景呢
- C语言实现冒泡排序(bubble排序)算法(附完整源码)
- oracle pga的作用,浅析Oracle中PGA和UGA两者间的区别
- 附近有什么?8款可以查周边的App
- Appium移动端自动化测试之元素定位(三)
- Group By和Order By的总结
- mysql数据库cms数据库文件_PbootCMS 默认数据库转Mysql数据库教程 - 老蒋SEO博客
- Kali linux 学习笔记(十三)主动信息收集——端口扫描(UDP扫描、TCP扫描) 2020.2.22
- Java小开发(车辆信息管理系统)
- 牛客 Algorithm Choosing Mushrooms
- 80004005错误代码_电脑出现错误代码0x80004005的解决方法
- php更换wordpress用户头像,WordPress修改评论默认头像的方法
- 制造行业实施作业成本法案例(AMT 邓为民)
- vim 变成只读了_VIM以只读方式打开文件
- 帝国CMS 批量修改信息标题方法
- TextBoxes++
- 3D建模教程:3DMAX制作电视机模型!
- 利用 OpenWrt 共享局域网下的佳能 MG2580s/MX398打印扫描一体机