Неуправляемая память, выделенная с помощью Marshal.AllocHGlobal, не освобождается автоматически.
Поэтому размещение Marshal.FreeHGlobal в блоке finally действительно хорошая идея:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
try
{
Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);
SomeCommandThatCanThrowAnException();
}
finally
{
Marshal.FreeHGlobal(unmanagedPointer);
}
В найденных вами примерах, вероятно, для краткости не используется обработка ошибок.
Если вы выделяете неуправляемую память для долгосрочных целей (т. е. не освобождаете ее в рамках одного и того же метода), вам может быть интересно поместить указатель в объект, производный от SafeHandle (например, SafeBuffer).
SafeHandle реализует шаблон IDisposable, поэтому неуправляемый память будет освобождена, когда вы избавитесь от объекта или когда сборщик мусора соберет объект. SafeHandle также является производным от класса CriticalFinalizerObject, а это означает, что он получит специальную обработку от CLR, чтобы убедиться, что память действительно освобождена.
class HGlobal : SafeBuffer
{
public HGlobal(int cb)
: base(true)
{
this.SetHandle(Marshal.AllocHGlobal(cb));
this.Initialize((ulong)cb);
}
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(this.handle);
return true;
}
}
Пример:
using (var h = new HGlobal(buffer.Length))
{
h.WriteArray(0, buffer, 0, buffer.Length);
}
Примечание. SafeBuffer — настоящий зверь, поэтому рекомендуется соблюдать осторожность.
Примечание 2. SafeHandles хорошо работает с P/Invoke и полностью устраняет необходимость обхода IntPtrs.
SafeBuffers предназначены для безопасного управления неуправляемой памятью из C#, поэтому в зависимости от того, что вы делаете (выделение неуправляемой памяти для использования с P/Invoke или управление неуправляемой памятью из C#), вы должны выбрать SafeHandle или SafeBuffer в качестве базового класса соответствующим образом.
person
dtb
schedule
19.08.2010