Подавление оптимизации памяти 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
ключевое слово для закрепления определенных объектов, с желаемым эффектом.