Следует ли помещать Marshal.FreeHGlobal в блок finally, чтобы обеспечить удаление ресурсов?

У меня есть следующий блок кода:

IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);
SomeCommandThatCanThrowAnException();
Marshal.FreeHGlobal(unmanagedPointer);

Должен ли блок быть обернут в try, а команда FreeHGlobal помещена в блок finally. (В случае, если средняя команда выдает исключение).

Кажется, имеет смысл, что finally предотвратит утечку памяти в этом случае, однако из примеров, которые я нашел в Интернете, finally не используется. Возможно, ресурсы в любом случае автоматически удаляются (даже если они неуправляемые).


person vicsz    schedule 19.08.2010    source источник


Ответы (2)


Неуправляемая память, выделенная с помощью 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

Абсолютно. Она никогда не освобождается автоматически, это неуправляемая память.

person Hans Passant    schedule 19.08.2010