はじめに
今回はアンマネージドメモリを確保・破棄する方法について以下の3つを紹介したいと思います。
Marshal.AllocHGlobal
NativeMemory.Alloc
NativeMemory.AllocZeroed
概要
NativeMemory
は.NET 6
から登場したクラスです。Marshal.AllocHGlobal
と内部的には同じ動作になっているそうです。
.NET 6からNativeMemoryというクラスが新たに追加されました。その名の通り、ネイティブメモリを扱いやすくするものです。今までもMarshal.AllocHGlobalといったメソッド経由でネイティブメモリを確保することは可能であったので、何が違うのか、というと、何も違いません。実際NativeMemoryArrayの .NET 6以前版はMarshalを使ってますし。そして .NET 6 では Marshal.AllocHGlobal は NativeMemory.Alloc を呼ぶので、完全に同一です。
neue cc - NativeMemoryArray - .NET 6 APIをフル活用した2GB超えの巨大データを扱うライブラリ
Marshal.AllocHGlobal
public static IntPtr AllocHGlobal (int cb); public static IntPtr AllocHGlobal (IntPtr cb);
プロセスのアンマネージ メモリからメモリを割り当てます。
指定したバイト数(バイト数へのポインター)を使用して、プロセスのアンマネージ メモリからメモリを割り当てます。
返り値として新しく割り当てられたメモリへのポインターが返ってきます。
またGC
の管理下にないので、後述のFreeHGlobal(IntPtr)
メソッドを利用してメモリを解放してあげる必要があります。
Marshal.FreeHGlobal
public static void FreeHGlobal (IntPtr hglobal);
以前にプロセスのアンマネージ メモリから割り当てられたメモリを解放します。
NativeMemory.Alloc
public static void* Alloc (UIntPtr byteCount); public static void* Alloc (UIntPtr elementCount, UIntPtr elementSize);
指定したサイズのメモリ ブロックをバイト単位(要素単位)で割り当てます。
返り値として割り当てられたメモリブロックへのポインターが返ってきます。
またこちらもGC
管理下にないので、NativeMemory.Free
を用いて解放してあげる必要があります。
NativeMemory.AllocZeroed
public static void* AllocZeroed (nuint byteCount); public static void* AllocZeroed (nuint elementCount, nuint elementSize);
指定したサイズのメモリ ブロックをバイト単位(要素単位)で割り当ててゼロにします。
NativeMemory.Alloc
との違いは、確保したメモリを0
で初期化するところです。
やり方
public static void Main(string[] args) { // UnManagedなHeap Memory確保(1) // Marshal.AllocHGlobal + Marshal.FreeHGlobalパターン // NONE: ゼロ初期化されている保証はない unsafe { // 確保 IntPtr p = Marshal.AllocHGlobal(sizeof(int) * 10); // 利用しやすいようにSpanに変換 Span<int> nativeArray = new Span<int>((int*)p, 10); nativeArray[0] = 100; // 100,0,0,0,0,0,0,0,0,0 Console.WriteLine(string.Join(",", nativeArray.ToArray())); // 解放 Marshal.FreeHGlobal(p); } // UnManagedなHeap Memory確保(2) // NativeMemory.Alloc + NativeMemory.Freeパターン // NONE: ゼロ初期化されている保証はない unsafe { // 確保 void* p = NativeMemory.Alloc(sizeof(int) * 10); // 利用しやすいようにSpanに変換 Span<int> nativeArray = new Span<int>((int*)p, 10); nativeArray[0] = 100; // 100,0,0,0,0,0,0,0,0,0 Console.WriteLine(string.Join(",", nativeArray.ToArray())); // 解放 NativeMemory.Free(p); } // UnManagedなHeap Memory確保(3) // NativeMemory.AllocZeroed + NativeMemory.Freeパターン // NOTE: セロ初期化されている unsafe { // 確保 void* p = NativeMemory.AllocZeroed(sizeof(int) * 10); // 利用しやすいようにSpanに変換 Span<int> nativeArray = new Span<int>((int*)p, 10); nativeArray[0] = 100; // 100,0,0,0,0,0,0,0,0,0 Console.WriteLine(string.Join(",", nativeArray.ToArray())); // 解放 NativeMemory.Free(p); } }