Как заставить JIT расширять переменные стека до конца области видимости (GC слишком быстр) - PullRequest
2 голосов
/ 01 августа 2011

Мы имеем дело с тем, что GC слишком быстр в программе .Net. Поскольку мы используем класс с нативными ресурсами и не вызываем GC.KeepAlive (), GC собирает объект до того, как закончится собственный доступ. В результате программа вылетает. У нас есть именно такая проблема, как описано здесь:

Выполняет ли сборщик мусора .NET прогнозный анализ кода?

Вот так:

{  var img = new ImageWithNativePtr();
   IntPtr p = img.GetData();
   // DANGER!
   ProcessData(p);
}

Дело в том, что JIT генерирует информацию, которая показывает GC, что img не используется в момент запуска GetData (). Если GC-поток приходит в нужное время, он собирает img и программа вылетает. Это можно решить, добавив GC.KeepAlive (img); К сожалению, уже написано слишком много кода (в слишком многих местах), чтобы легко устранить проблему.

Следовательно: существует ли, например, Атрибут (то есть для ImageWithNativePtr), чтобы заставить JIT вести себя как в сборке отладки? В сборке Debug переменная img будет оставаться действительной до конца области видимости (}), а в Release она теряет допустимость при комментарии DANGER.

Ответы [ 3 ]

2 голосов
/ 01 августа 2011

Насколько мне известно, нет способа контролировать поведение джиттера в зависимости от того, на какие типы ссылается метод. Возможно, вы сможете приписать сам метод, но это не будет выполнять ваш заказ. В таком случае вам нужно стиснуть зубы и переписать код. GC.KeepAlive это один из вариантов. Другой способ - заставить GetData возвращать безопасный дескриптор, который будет содержать ссылку на объект, и ProcessData принимать дескриптор вместо IntPtr - в любом случае, это хорошая практика. GC будет хранить безопасный дескриптор до тех пор, пока метод не вернется Если большая часть вашего кода имеет var вместо IntPtr, как в фрагменте кода, вы можете даже избежать изменения каждого метода.

1 голос
/ 01 августа 2011

У вас есть несколько вариантов.

  1. (Требуется работа, точнее) - Реализуйте IDisposable в вашем классе ImageWithNativePtr, так как он компилируется до try { ... } finally { object.Dispose() }, который будет сохранять объектжив, если вы обновите свой код с using s.Вы можете облегчить эту задачу, установив что-то вроде CodeRush (даже бесплатный Xpress поддерживает это) - который поддерживает создание блоков using.
  2. (проще, не правильно, более сложноbuild) - используйте Post Sharp или Mono.Cecil и создайте свой собственный атрибут.Обычно этот атрибут приводит к вставке GC.KeepAlive () в эти методы.

В CLR ничего не встроено для этой функции.

0 голосов
/ 01 августа 2011

Я считаю, что вы можете эмулировать то, что вы хотите, с помощью контейнера, который реализует IDispose, и оператора using.Оператор using позволяет определить область действия, и вы можете поместить в нее все, что захотите, и которая должна быть активной в этой области.Это может быть полезным механизмом, если у вас нет контроля над реализацией ImageWithNativePtr.

Контейнер для утилизации - полезная идиома.Особенно, когда вы действительно должны избавиться от чего-то ... что, вероятно, имеет место с изображением.

using(var keepAliveContainer = new KeepAliveContainer())
{
  var img = new ImageWithNativePtr();
  keepAliveContainer.Add(img);
  IntPtr p = img.GetData();
  ProcessData(p);
  // anything added to the container is still referenced here.
}
...