08. Flutter FFI 메모리 관리

 
Flutter FFI 연구 노트 시리즈

  1. "Flutter FFI의 가장 간단한 예"
  2. "Flutter FFI 기본 데이터 유형"
  3. "플러터 FFI 기능"
  4. "플러터 FFI 문자열"
  5. "플러터 FFI 구조"
  6. "플러터 FFI 클래스"
  7. "플러터 FFI 어레이"
  8. "플러터 FFI 메모리 관리"
  9. 《플러터 FFI 다트 네이티브 API》
      
     

  이전 장에서는 기본 데이터 유형, 문자열, 구조, 클래스 및 배열과 같은 지식 포인트를 소개했으며 다음으로 FFI의 메모리 관리를 소개합니다.
  C 언어 개발 과정에서 메모리의 적용과 복구는 개발자 자신의 몫이며, 이전 글에서 메모리 할당과 복구에 대해 많이 설명하였으니 오늘 계속해서 알아보도록 하겠습니다.
  
 

1. 메모리 관리 소개

  Dart FFI는 개발자가 Dart 코드를 사용하여 네이티브에서 메모리를 적용하고 해제할 수 있도록 하는 몇 가지 API를 제공합니다. 이러한 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();

  코드 설명 :

  • 위의 mallocand는 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위해 . malloccalloc의 내부가 이미 이 작업을 수행했기 때문입니다.
  • 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의 다양한 편의 메서드를 사용할 수 있습니다 .foreachwhere

 

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. 자동 해제 풀 - 아레나

  때로는 일부 작업을 수행하기 위해 일시적으로 메모리를 적용하고 작업이 완료된 후 메모리를 해제해야 하지만 종종 메모리 해제를 잊어버리거나 free(a)잘못된 메모리를 작성하여 free(b).
  실제로 Dart는 위의 사용 시나리오를 처리할 수 있는 자동 릴리스 풀을 구현했습니다. 그것
  
  Arena 과 의 가장 큰 차이점 은 그것 에 의해 할당된 메모리 를 자동 으로 해제 한다는 것입 니다 . 내부적으로는 기본적 으로 , 메모리를 신청할 때마다 기록하고 나중에 호출할 수 있는 모든 메서드는 해제됩니다. 핵심 코드는 다음과 같습니다.Allocator _MallocAllocator_CallocAllocator
  ArenacallocreleaseAll()
  Arena

class Arena implements Allocator {
    
    
    
    //持有一个Allocator,用于实际的内存分配和回收
    final Allocator _wrappedAllocator;
    
    //这个List用于记录已分配的内存的指针
    final List<Pointer<NativeType>> _managedMemoryPointers = [];
    
    //构造函数,默认使用 calloc
    Arena([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
    @override
    void 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() function 은 메모리를 해제해야 하는 경우 호출해야 하는 무효 함수입니다 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 및 기타 고급 사용법을 구현하는 방법을 소개합니다. 환영합니다.

  
 

Guess you like

Origin blog.csdn.net/eieihihi/article/details/119600283