Flutter FFI 学习笔记系列

  1. 《Flutter FFI 最简示例》
  2. 《Flutter FFI 基础数据类型》
  3. 《Flutter FFI 函数》
  4. 《Flutter FFI 字符串》
  5. 《Flutter FFI 结构体》
  6. 《Flutter FFI 类》
  7. 《Flutter FFI 数组》
  8. 《Flutter FFI 内存管理》
  9. 《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 不会;
  • 数组指针可以转为 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 已经为我们实现了一个自动释放池,可以应对上述使用场景。
  
  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 完成内存的自动释放。

//创建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 内存管理相关推荐

  1. 01、Flutter FFI 最简示例

    Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...

  2. 03、Flutter FFI 函数

    Flutter FFI 学习笔记系列 <Flutter FFI 最简示例> <Flutter FFI 基础数据类型> <Flutter FFI 函数> <Fl ...

  3. Dart 内存管理机制

    Dart汇总请点击这里 文章目录 Dart内存管理 Dart运行环境(VM) 新生代(New Generation) 老年代(Old Generation) 内存管理算法 iOS端 Android端 ...

  4. [objective-c] 08 - 内存管理

    OC语言中的内存管理机制为ARC(Automatic Reference Counting,自动引用计数).于2011年中旬推出,替换陈旧且低效的手动内存管理,关于手动内存管理的内容,本章教程不再讲授 ...

  5. 一文搞懂栈(stack)、堆(heap)、单片机裸机内存管理malloc

    大家好,我是无际. 有一周没水文了,俗话说夜路走多了难免遇到鬼. 最近就被一个热心网友喷了. 说我的文章没啥营养,所以今天来一篇烧脑的. 哈哈,开个玩笑,不要脸就没人能把我绑架. 主要是最近研发第二代 ...

  6. Objective-C 内存管理之ARC规则

    基本概念 ARC为自动引用计数,引用计数式内存管理的本质并没有改变,ARC只是自动处理"引用计数"的相关部分. 在编译上,可以设置ARC有效或无效.默认工程中ARC有效,若设置无效 ...

  7. python内存管理错误的是_关于Python内存管理,下列说法错误的是

    53 Python中变量可以不指定类型,会自动根据赋值语句来决定类型 同时,使用前必须赋值,在赋值的同时变量也就创建了 发表于 2018-02-28 22:50:02 回复(0) 61 本题答案选 B ...

  8. (0048)iOS开发之内存管理探究

    注意网上很多人支招在ARC模式下可以利用_objc_rootRetainCount()或者CFGetRetainCount()取得 retainCount都是不准确的,特别是在对象拷贝操作之后你会发现 ...

  9. linux 内存管理 page fault带来的性能问题

    Linux进程如何访问内存 Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源. 原因后面会讲到. 为什么需要虚拟内存地址空间 假设某个进程需要4MB的空间,内存假 ...

  10. [内存管理] linux kernel内存映射实例分析

    作者:JHJ(jianghuijun211@gmail.com ) 日期:2012/08/24 欢迎转载,请注明出处 引子 现在android智能手机市场异常火热,硬件升级非常迅猛,arm corte ...

最新文章

  1. java threadgourp_Java Thread getThreadGroup()方法
  2. etcd集群部署与遇到的坑
  3. 启明云端分享|ESP32-S3一颗融合AI和IOT于一体的芯片,到底有哪些功能!能应用在哪些场景呢
  4. C语言实现冒泡排序(bubble排序)算法(附完整源码)
  5. oracle pga的作用,浅析Oracle中PGA和UGA两者间的区别
  6. 附近有什么?8款可以查周边的App
  7. Appium移动端自动化测试之元素定位(三)
  8. Group By和Order By的总结
  9. mysql数据库cms数据库文件_PbootCMS 默认数据库转Mysql数据库教程 - 老蒋SEO博客
  10. Kali linux 学习笔记(十三)主动信息收集——端口扫描(UDP扫描、TCP扫描) 2020.2.22
  11. Java小开发(车辆信息管理系统)
  12. 牛客 Algorithm Choosing Mushrooms
  13. 80004005错误代码_电脑出现错误代码0x80004005的解决方法
  14. php更换wordpress用户头像,WordPress修改评论默认头像的方法
  15. 制造行业实施作业成本法案例(AMT 邓为民)
  16. vim 变成只读了_VIM以只读方式打开文件
  17. 帝国CMS 批量修改信息标题方法
  18. TextBoxes++
  19. 3D建模教程:3DMAX制作电视机模型!
  20. 利用 OpenWrt 共享局域网下的佳能 MG2580s/MX398打印扫描一体机

热门文章

  1. 古城钟楼的微博报时是如何实现的?[科普贴]
  2. JAVA导出exls时报oom,Poi导出产生OOM解决方案
  3. 亚马逊云科技 Build On -轻松搭建咖啡点单系统
  4. UCT(信心上限树算法)解四子棋问题——蒙特卡罗法模拟人机博弈
  5. 揭秘:全球第一张云安全国际认证金牌得主
  6. 纠结火锅去哪吃,用Python可视化做个数据呈现决定吧(附学习资源)
  7. 85-94年河南农村娃们的童年记忆,追忆童年旧时光
  8. php阿里支付回调逻辑,php 银联支付回调
  9. 聚合支付公司利楚扫呗获得腾讯、蚂蚁亿元入股
  10. 颜色模式,tiff,rgb2cmyk