失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 08 Flutter FFI 内存管理

08 Flutter FFI 内存管理

时间:2020-11-30 09:30:51

相关推荐

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()malloccalloc的一个重大区别是: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();

代码说明

上面的malloccalloc是两个实例,可以在任意地方调用;

2、内存分配与释放

前面介绍了使用 Allocator 类进行内存的申请和释放,现在来介绍如何使用malloccalloc这两个对象来分配内存、释放内存。

2.1 内存分配与释放

下面的示例中,演示了如何使用malloccalloc来申请内存和释放内存:

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来加载库,因为malloccalloc的内部已经帮我们做了一这步了;调用allocate()函数时,需要明确指定byteCount,这里我们通过sizeOf()函数来获取Int32的字节数;调用call()函数时,不需要指定byteCountcall()函数内部已经帮我们调用了sizeOf()函数了;从上面的示例可以看出,calloc申请内存之后会初始化为0,而malloc则不会;malloccalloc两者的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不会;数组指针可以转为 DartList,这样就可以使用 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 已经为我们实现了一个自动释放池,可以应对上述使用场景。

ArenaAllocator的另一个实现类,它与_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完成内存的自动释放。

//创建ArenaArena 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 内存管理》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。