Как расположить класс в .net? - PullRequest
42 голосов
/ 15 августа 2008

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

MyClass.Dispose()

и освободите все использованное пространство переменными и объектами в MyClass?

Ответы [ 20 ]

1 голос
/ 31 августа 2008

@ Keith,

Я согласен со всеми вашими правилами, кроме # 4. Добавление финализатора должно быть сделано только при очень определенных обстоятельствах. Если класс использует неуправляемые ресурсы, они должны быть очищены в вашей функции Dispose (bool). Эта же функция должна очищать только управляемые ресурсы, когда bool имеет значение true. Добавление финализатора увеличивает стоимость использования вашего объекта, так как каждый раз, когда вы создаете новый экземпляр, он также должен помещаться в очередь финализации, которая проверяется каждый раз, когда GC запускает цикл сбора. Фактически это означает, что ваш объект выживает на один цикл / поколение дольше, чем следовало бы, поэтому финализатор может быть запущен. Финализатор не должен рассматриваться как «сеть безопасности».

GC будет запускать цикл сбора данных только тогда, когда он определит, что в куче Gen0 недостаточно доступной памяти для выполнения следующего выделения, если только вы не «поможете», вызвав GC.Collect (), чтобы вызвать коллекция.

Суть в том, что, несмотря ни на что, GC знает, как освобождать ресурсы, только вызывая метод Dispose (и, возможно, финализатор, если таковой реализован). Именно этот метод "делает правильные вещи" и очищает все неуправляемые используемые ресурсы и дает указание другим управляемым ресурсам вызывать их метод Dispose. Он очень эффективен в том, что делает, и может в значительной степени самооптимизироваться, если ему не помогают циклы внеполосного сбора. При этом, за исключением явного вызова GC.Collect, вы не можете контролировать, когда и в каком порядке будут удаляться объекты и освобождаться память.

1 голос
/ 26 августа 2008

Извините, но выбранный ответ здесь неверен. Как впоследствии несколько человек заявили, Dispose и реализация IDisposable не имеют ничего общего с освобождением памяти, связанной с классом .NET. Он в основном и традиционно используется для освобождения неуправляемых ресурсов, таких как дескрипторы файлов и т. Д.

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

Вы не говорите в своем вопросе, ПОЧЕМУ вы чувствуете необходимость немедленно освободить память. Я понимаю, что иногда могут возникать необычные обстоятельства, но серьезно, в управляемом коде почти всегда лучше, чтобы среда выполнения работала с управлением памятью.

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

0 голосов
/ 21 августа 2008

У вас может быть детерминированное уничтожение объектов в c ++

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

Для тех, кто публикует идентифицируемые ответы. Вызов метода Dispose не уничтожает объект, как описывает Аскер.

0 голосов
/ 23 марта 2012

Отвечая на оригинальный вопрос, с информацией, предоставленной до сих пор оригинальным постером, он на 100% уверен, что он не знает достаточно о программировании в .NET, чтобы даже получить ответ: используйте GC.Collect () , Я бы сказал, что с вероятностью 99,99% ему вообще не нужно использовать GC.Collect (), как указывалось в большинстве постеров.

Правильный ответ сводится к: «Пусть GC сделает свою работу. Период. У вас есть другие вещи, чтобы беспокоиться о. Но вы, возможно, захотите подумать, нужно ли и когда удалять или очищать определенные объекты, и нужно ли вам реализовать IDisposable и, возможно, Finalize в вашем классе. '

Относительно должности Кейта и его Правила № 4:

Некоторые постеры сбивают с толку правило 3 и правило 4. Правило Кейта 4 абсолютно правильно, однозначно. Это единственное правило из четырех, которое вообще не нуждается в редактировании. Я бы немного перефразировал некоторые другие его правила, чтобы прояснить их, но они, по сути, правильны, если вы их правильно проанализируете, и прочитаете весь пост, чтобы увидеть, как он их расширяет.

  1. Если ваш класс не использует неуправляемый ресурс И он также никогда не создает экземпляр другого объекта класса, который сам использует, прямо или в конечном счете, неуправляемый объект (т. Е. Класс, реализующий IDisposable), тогда не нужно, чтобы ваш класс сам реализовывал IDisposable или даже вызывал .dispose для чего-либо. (В таком случае глупо думать, что вам действительно нужно немедленно освободить память с помощью принудительного сбора данных, в любом случае.)

  2. Если ваш класс использует неуправляемый ресурс, ИЛИ создает экземпляр другого объекта, который сам реализует IDisposable, то ваш класс должен либо:

    a) немедленно утилизировать / освободить их в локальном контексте, в котором они были созданы, ИЛИ ...

    b) внедрить IDisposable в соответствии с шаблоном, рекомендованным в сообщении Кейта, или в нескольких тысячах мест в Интернете, или буквально в 300 книгах к настоящему времени.

    b.1) Кроме того, если (b), и это неуправляемый ресурс, который был открыт, как IDisposable, так и Finalize ДОЛЖНЫ быть ВСЕГДА реализованы, согласно правилу Кейта # 4.
    В этом контексте Finalize абсолютно ЕСТЬ сеть безопасности в одном смысле: если кто-то создает экземпляр ВАШЕГО IDisposable объекта, который использует неуправляемый ресурс, и ему не удается вызвать dispose, то Finalize - это последний шанс для ВАШЕГО объекта правильно закрыть неуправляемый ресурс.
    (Finalize должен делать это, вызывая Dispose таким образом, чтобы метод Dispose пропускал освобождение чего-либо, НО неуправляемый ресурс. В качестве альтернативы, если метод Dispose вашего объекта правильно вызывается любым экземпляром вашего объекта, то он ОБА передает вызов Dispose для все объекты IDisposable, в которых он создан, И освобождает неуправляемые ресурсы должным образом, заканчивая вызовом для подавления Finalize на вашем объекте, что означает, что влияние использования Finalize уменьшается, если вызывающий объект правильно расположил ваш объект. включены в сообщение Кита, кстати.)

    b.2) ЕСЛИ ваш класс реализует только IDisposable, потому что ему необходимо по существу передать Dispose для объекта IDisposable, который он создал, а затем не реализовывать метод Finalize в вашем классе в этом случае. Finalize предназначен для обработки случая, когда BOTH Dispose никогда не вызывался каким-либо экземпляром вашего объекта, И был использован неуправляемый ресурс, который все еще не выпущен.

Короче говоря, в отношении поста Кита он совершенно прав, и этот пост, на мой взгляд, является наиболее правильным и полным ответом. Он может использовать некоторые краткие заявления, которые некоторые находят «неправильными» или на которые они возражают, но его полный пост полностью раскрывает использование Finalize, и он абсолютно прав. Обязательно прочитайте его пост полностью, прежде чем прыгать по одному из правил или предварительных утверждений в его посте.

0 голосов
/ 15 августа 2008

Если MyClass реализует IDisposable, вы можете сделать это.

MyClass.Dispose();

Лучшая практика в C #:

using( MyClass x = new MyClass() ) {
    //do stuff
}

Поскольку это завершает распоряжение в конце попытки и гарантирует, что оно никогда не пропущено.

0 голосов
/ 15 августа 2008

Если вы не хотите (или не можете) реализовать IDisposable в своем классе, вы можете принудительно выполнить сборку мусора следующим образом (но это медленно) -

GC.Collect();
0 голосов
/ 26 августа 2008

Конрад Рудольф - да, обычно финализатор вообще ничего не делает. Вы не должны реализовывать это, если не имеете дело с неуправляемыми ресурсами.

Затем, когда вы реализуете его, вы используете Шаблон избавления от Microsoft (как уже описано)

  • public Dispose() звонки protected Dispose(true) - касается как управляемых, так и неуправляемых ресурсов. Вызов Dispose() должен подавить завершение.

  • ~Finalize звонки protected Dispose(false) - касается только неуправляемых ресурсов. Это предотвращает неуправляемые утечки памяти, если вы не можете вызвать public Dispose()

~Finalize работает медленно и не должен использоваться, если у вас нет неуправляемых ресурсов для работы.

Управляемые ресурсы не могут утечь память, они могут только тратить ресурсы на текущее приложение и замедлять сборку мусора. Неуправляемые ресурсы могут просачиваться, и ~Finalize является наилучшей практикой, гарантирующей, что они не будут.

В любом случае using - лучшая практика.

0 голосов
/ 26 августа 2008

@ Keith:

IDisposable для управляемых ресурсов.

Финализаторы предназначены для неуправляемых ресурсов.

Извините, но это просто неправильно. Обычно финализатор вообще ничего не делает. Однако, если шаблон dispose был правильно реализован, финализатор пытается вызвать Dispose.

Dispose имеет две работы:

  • бесплатные неуправляемые ресурсы и
  • бесплатные вложенные управляемые ресурсы.

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

Тем не менее, финализаторы не имеют никакой работы, кроме как вызвать Dispose и сказать ему не трогать управляемые объекты. Dispose при вызове вручную (или через Using) освобождает все неуправляемые ресурсы и передает сообщение Dispose во вложенные объекты (и методы базового класса), но это никогда не освобождает любого ( управляемая) память.

0 голосов
/ 26 августа 2008

@ Курт Хагенлохер - это задом наперед. Я понятия не имею, почему так много проголосовало, когда это неправильно.

IDisposable для управляемых ресурсов.

Финализаторы предназначены для неуправляемых ресурсов.

Пока вы используете только управляемые ресурсы, @Jon Limjap и я абсолютно правы.

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

Избегайте использования GC.Collect - это медленный способ работы с управляемыми ресурсами, и он ничего не делает с неуправляемыми, если вы правильно не построили свои ~ финализаторы.


Я удалил комментарий модератора из исходного вопроса в соответствии с https://stackoverflow.com/questions/14593/etiquette-for-modifying-posts

0 голосов
/ 15 августа 2008

IDisposable интерфейс действительно для классов, которые содержат неуправляемые ресурсы. Если в вашем классе нет неуправляемых ресурсов, зачем вам нужно , чтобы освободить ресурсы до того, как это сделает сборщик мусора? В противном случае просто убедитесь, что ваш объект создан как можно позже и выходит из области видимости как можно скорее.

...