Какова продолжительность жизни структур, когда они возвращаются из метода и передаются в качестве параметров? - PullRequest
2 голосов
/ 28 ноября 2011

Я попытаюсь немного больше разобраться в этом вопросе на примере:

У меня есть структура:

struct RefStruct
{
   public object token;
   public object item;
}

, и у меня есть метод, возвращающий эту структуру:

RefStruct createItem() {... }

Предположим, что createItem генерирует элемент и токен, где указанный токен содержит информацию, используемую элементами 'item' и 'item', ссылающуюся на один и тот же токен внутри через WeakReference.

Теперь, если я вызываю этот код (при условии, что doSomething обрабатывает Item и требует, чтобы токен был живым):

{
   ...
   doSomething(createItem().item);
   ...
}

-Пожалуйста, обратите внимание, что вызов получает элемент, а не всю структуру.

Гарантируется ли, что результирующий RefStruct, восстановленный 'createItem', будет храниться в памяти во время вызова doSomething?или эта ссылка отбрасывается CLR, и теперь на нее ссылается только элемент (что позволяет использовать временную структуру GCed)?

надеюсь, что это имеет смысл; p

Ответы [ 4 ]

11 голосов
/ 28 ноября 2011

Предположим, что 'createItem' генерирует элемент и токен, где указанный токен необходим, чтобы остаться в живых (т.е. не GCed), чтобы 'item' был действительным.

Ваш дизайн в корне сломан. Если токен должен быть живым, чтобы предмет был действительным, тогда в предмете должна быть ссылка на токен.

Исправьте свой дизайн. Либо отделите действительность предмета от срока действия токена, либо удерживайте предмет на токене, либо сделайте третье, что удерживает их обоих. (А потом оставь в живых третье.)

Гарантируется ли, что результирующий RefStruct, восстановленный 'createItem', будет сохранен в памяти во время вызова doSomething?

Абсолютно нет. Сборщик мусора имеет право собирать токен не только за до вызова DoSomething , но на самом деле до того, как CreateItem вернет . Сборщику мусора разрешается произвольно заглядывать в будущее и прогнозировать будущее поведение программы. Если с помощью анализа он может определить, что к объекту токена никогда не обращаются, то ему разрешается освободить его в момент после его создания .

Реализуется ли это в любой реализации CLR, это деталь реализации, которая может быть изменена.

Если я сохраню результат 'CreateItem' во временной структуре и передам свойство 'item' в 'doSomething', оптимизатору все еще позволят освободить токен раньше?

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

Само хранилище struct само по себе не будет восстановлено досрочно; Вы можете, например, позже записать в часть поля токена локального хранилища. Но память для объекта, на который ссылается поле token , может быть восстановлена ​​в любое время.

Изменит ли использование volatile на члене 'token' это?

Нет. Изменчивые поля имеют семантику получения и выпуска при записи и чтении; это не имеет никакого отношения к жизни поля.

Можете ли вы объяснить, почему вы полагаете, что превращение поля в изменчивое изменило бы его время жизни? Мне всегда интересно узнать, почему люди верят в причудливые вещи.

Я хотел бы гарантировать, что пока я держу токен, он не будет GCed, чтобы избежать GC во время операции doSomething

Теперь мы подошли к актуальной проблеме. До сих пор вы задавали вопросы о том, как работают буровые двигатели, а не о том, какое отверстие вы хотите сверлить. Если вопрос «как мне сохранить эту мертвую вещь?» Ответ на этот вопрос прост. Звоните GC.KeepAlive . Вот для чего.

Как ясно сказано в документации:

Цель метода KeepAlive - обеспечить наличие ссылки на объект, который может быть преждевременно утилизирован сборщиком мусора.

Теперь, разумный вопрос в этой точке: «Но если токен может быть забран, то как возможно, что кто-то может использовать его для повышения эффективности предмета?» Вы отвечаете на этот вопрос в этом комментарии:

Это часть системы кеширования, и поскольку «токен» в strcut является надежной ссылкой, существуют другие WeakReferences, в то время как токен не собран, известно, что кэшированный элемент необходим и его не следует удалять из кэш

Вы назначаете сборщик мусора ответственным за соблюдение политики кэширования . Сборщик мусора не был разработан, чтобы иметь хорошее поведение для вашего сценария; сборщик мусора был разработан, чтобы иметь хорошее поведение для общей проблемы управления памятью на языке программирования общего назначения.

Я бы очень серьезно подумал, прежде чем поставить механизм управления жизненным циклом, разработанный одной группой людей для решения их общей проблемы, отвечающей за определение политики для потенциально совершенно другого пространства проблем. Тот факт, что вы задаете подобные вопросы, свидетельствует о том, что вам нужно создать собственный код принудительного применения политики кэширования, а не полагаться на то, что GC сделает это за вас способом, который вам не нравится.

Supercat спрашивает:

Не возникнет ли точно такая же проблема, если RefStruct был заменен на класс?

Да. Если вы делаете, скажем, это:

Tuple<object, object> holder = CreateItem(...); 
DoSomething(holder.Item1);

Опять же, GC полностью в своих правах, чтобы определить, что "держатель" мертв, и, следовательно, holder.Item2 мертв, момент, когда CreateItem завершается выполнение. Как я уже сказал, вам нужно сохранить «владельца» - «третью вещь» - живым. GC допускается широкая широта в определении того, что еще живо.

5 голосов
/ 28 ноября 2011

Структуры не имеют времени жизни; вещи, которые держат структуры, имеют время жизни.
(хотя в штучной упаковке структуры имеют время жизни)

Ваш токен может быть немедленно собран GC, поскольку на него никогда не ссылаются.

1 голос
/ 28 ноября 2011

Могут быть некоторые законные причины, чтобы RefStruct был структурой, а не классом, но независимо от того, является ли это структурой или классом, необходимо иметь вызов GC.KeepAlive, чтобы гарантировать, что токен не будет собран в то время как другой код выполняется на элементе. Я хотел бы предложить, чтобы вместо непосредственного предоставления Item можно было использовать общий метод DoSomethingWithItem, который принимает делегат и универсальный параметр ref, и который будет вызывать предоставленный делегат для Item, а затем GC.KeepAlive Token. Если универсальный метод принимает универсальный параметр ref, в большинстве сценариев использования он позволяет использовать статический делегат и помещать любые необходимые входные или выходные параметры в тип значения, что позволяет избежать давления ГХ.

1 голос
/ 28 ноября 2011

Нет, RefStruct может сразу GCed. Даже делая это:

var rs = createItem();
doSomething(rs.item);

не гарантирует сохранность токена.

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

class RefStruct : IDisposable
{
   public object token;
   public object item;

   public void Dispose() 
   { 
       Dispose(true);
       GC.SuppressFinalize(this);
   }

   protected virtual void Dispose(bool disposing)
   {
        if (disposing)
        {
            token = null; // not necessary technically
        }
    }
}

А затем используйте его:

using (var rs = createItem())
{
    doSomething(rs.Item);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...