Подавление оптимизации памяти GC

Я пишу программу, которая вызывает несколько функций на основе C (p/Invoke) в нескольких потоках.

Время от времени программа вылетает с ошибкой нарушения прав доступа. Моей первой мыслью было, что GC оптимизировал память и переместил часть памяти, над которой работала функция C, в другое место.

Я хотел бы, чтобы GC работал, но отключал часть, в которой он перемещает (дефрагментирует) память.

Есть ли способ сделать это?

3 ответа

Как уже говорили другие ответы, первое, что нужно сделать, это убедиться, что вы правильно закрепляете объекты. Предполагая, что вы сделали это, что еще может пойти не так?

class C
{
    public int handle;
    ...
    ~C() { InteropLibrary.DestroyHandle(handle); }
}

void M()
{
    C c = GetSomeObjectUsefulInUnmanagedCode();
    D d = InteropLibrary.UnmanagedMethodThatUsesHandle(c);
    // COMMENT
    d.DoSomethingWithStoredHandle();
}

Что делать, если сборка мусора происходит в COMMENT (*)? Сборщик мусора может свободно сказать "эй, локальная переменная c никогда не упоминается снова в этом методе; Я могу быть агрессивным и обращаться с ним как с мертвым!". Если финализатор запускается и дескриптор уничтожается, то при запуске последнего метода он получает доступ к уничтоженному дескриптору и вылетает.

Чтобы решить эту редкую, но возможную проблему, вы можете использовать GC.KeepAlive сказать сборщику мусора быть менее агрессивным в отношении очистки конкретной ссылки. Если вы продолжаете c жив до конца метода, тогда вы знаете, что его деструктор не может работать.


(*) Конечно, GC работает в другом потоке и может работать в любое время. Детали того, какие операции являются и не являются прерываемыми GC, сложны, и вы не должны полагаться на эти виды деталей реализации для правильности.

Вы можете использовать fixed Ключевое слово в большинстве случаев.

Из нового блога Эрика Липперта кажется, что есть как минимум две другие возможности:

  • используя GCHandle для выделения памяти
  • используя внешний неуправляемый распределитель, такой как AllocHGlobal

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

Напомним, что если ваша проблема заключается в том, что крошечный объем памяти перемещается (что приводит к нарушениям доступа), то решением почти никогда не является отключение всей части перемещения памяти.

Я не думаю, что вы можете сделать это глобально, но вы можете использовать fixed ключевое слово для закрепления определенных объектов, с желаемым эффектом.

Другие вопросы по тегам