Существует ли распространенная практика облегчения освобождения памяти для сборщика мусора в .NET? - PullRequest
1 голос
/ 26 апреля 2010

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

Например, полезно ли присваивать нулевое значение объектам, которые больше не нужны? Я вижу это в нескольких примерах через Интернет.

Ответы [ 6 ]

6 голосов
/ 26 апреля 2010

полезно ли присваивать нулевое значение объектам, которые больше не нужны?

Как правило, нет. Большинство примеров, которые вы увидите в Интернете, сделаны людьми, пришедшими на .Net из VB6, где это было распространено. В .Net это менее полезно. Если у вас очень долго работающий метод, он может позволить сборщику мусора найти объект немного раньше & mdash; но если ваш метод так долго, у вас есть другие проблемы.

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

Вы должны не делать свою собственную сборку мусора, как предлагает хотя бы один ответ здесь. Это может на самом деле сделать вещи медленнее . .Net использует сборщик мусора поколений. Заставив сборщик мусора, вы могли бы собирать целевой объект (вы также можете этого не делать - с сборкой мусора нет никаких гарантий). Но вы, вероятно, также заставите кучу других объектов, которые вы еще не можете собрать, сохранить в поколении более высокого порядка, что затруднит их сбор в будущем. Так что просто не делай этого.

4 голосов
/ 26 апреля 2010

Вы не должны слишком беспокоиться и не оптимизировать преждевременно. Управление памятью в .NET происходит автоматически и очень эффективно. Если вы начинаете «оптимизировать», вам нужно точно знать, что вы делаете, или вы можете замедлить его.

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

3 голосов
/ 26 апреля 2010

Большинство графических объектов в .net (изображения и его потомки, графика, перо, кисть и т. Д.) Реализуют IDisposable Если вы собираетесь использовать объект для определенной операции, то не снова используйте следующий шаблон:

using(var g = Graphics.FromBitmap(bmp))
{
    //Do some stuff with the graphics object
}

Если вы сделаете это так, все неуправляемые ресурсы будут освобождены, когда они выйдут из области видимости.

1 голос
/ 09 февраля 2011

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

Объекты, содержащие поля, объявленные как «WithEvents» в vb.net, должны реализовывать IDisposable, и должны обнулять эти поля в методе Dispose. Я не знаю, почему vb.net не включает в себя хороший способ автоматического обнуления полей WithEvents, но так как они не должны быть очищены вручную. Если локальная переменная содержит ссылку на объект, который фактически никогда больше не будет использоваться, но может пройти некоторое время, прежде чем код достигнет точки, в которой компилятор знает, что переменная не будет использоваться, очистка переменной может позволить ссылка должна быть освобождена раньше. Если в какой-то момент массив, который является большим или существовал в течение некоторого времени, как известно, бесполезен, и если в нем хранятся ссылки на некоторые недавно выделенные объекты, очистите эти ссылки перед уничтожением всех сохранившихся ссылок на массив может позволить вновь созданным объектам быть освобожденными намного раньше, чем они были бы в противном случае. Если объект имеет процедуру Finalize и находится в очереди завершения, все объекты, на которые он прямо или косвенно ссылается, будут освобождены от сборки мусора. Следовательно, финализуемые объекты не должны ссылаться на то, что не потребуется для финализации.
1 голос
/ 26 апреля 2010

Я считаю, что выделение объектов в .net - одна из вещей, которая больше всего влияет на производительность. Чтобы обойти это, я использую шаблон Factory, где я перерабатываю объекты, которые я использовал. Вот простая универсальная реализация:

internal class ListFactory<T> 
    where T: IRecyclable, new()
{
    private List<T> _internalList;
    private int _pointer;

    public ListFactory()
    {
        _internalList = new List<T>();
        _pointer = 0;
    }

    public void Clear()
    {
            _pointer = 0;
    }

    public int Count
    {
        get
        {
            return _pointer;
        }
    }

    public List<T> InnerList
    {
        get
        {
            return _internalList;
        }
    }

    //Either return T form the list or add a new one
    //Clear T when it is being reused
    public T Create()
    {
        T t;

        //If the pointer is less than the object count then return a recycled object
        //Else return a new object 
        if (_pointer < _internalList.Count )
        {
            t = _internalList[_pointer];
            t.Recycle();
        }
        else
        {
            t = new T();
            _internalList.Add(t);
        }
        _pointer ++;
        return t;
    }
}

Для моего алгоритма линейной маршрутизации мне нужно постоянно хранить много значений в виде RouteNode , который реализует следующий интерфейс:

public interface IRecyclable
{
    void Recycle();
}

Они постоянно создаются и уничтожаются. Чтобы переработать эти объекты, создайте новую фабрику:

nodeFactory = new ListFactory<RouteNode>();

Когда вам нужен объект, вызовите метод create:

RouteNode start = nodeFactory.Create();
RouteNode goal = nodeFactory.Create();

Когда вы закончите с объектами в списке, очистите список. Указатель сбрасывается на начало списка, но сами объекты не уничтожаются. На самом деле метод Recycle вызывается только тогда, когда объект восстанавливается снова. (Возможно, вы захотите сделать это раньше, если у вас есть другие ссылки на объекты, см. Комментарии ниже)

Это довольно наивная реализация, но ее нужно начинать.

0 голосов
/ 26 апреля 2010

Чтобы сделать это (и мне приходилось делать это с частыми> 50 МБ выделениями), позвоните:

        myObj = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();

Я заметил, что объем памяти приложения значительно уменьшится. Теоретически вам не нужно этого делать. Однако на практике с 32-битной ОС Windows вы можете получить 2 смежных блока размером> 300 Мбайт в любой момент времени, и если это пространство будет занято множеством небольших или ряда больших выделений, это может означать, что другие большие выделения будут терпеть неудачу без необходимости. Сборщик мусора работает в фоновом режиме, когда это возможно, но если вам абсолютно необходимо сделать большие выделения прямо сейчас, этот набор строк помогает сделать это возможным для меня.

РЕДАКТИРОВАТЬ: Из того, что я положил в комментариях, для downvoters.

Если вы прочитаете весь пост о сборе мусора Рико Мариани , вы заметите, что большие, нечастые, непредсказуемые выделения памяти попадают в сценарий № 2. Кому:

Правило № 2

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

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

Теперь, почему я предложил бы это как возможное время позвонить коллекционеру? Я имею в виду, мой обычный совет идет что-то вроде "коллекционер самонастраивается так не связывайтесь с этим ". Почему изменения Вы можете спросить об отношении?

Ну вот ситуация, когда склонность коллекционера [sic] пытаться предсказать будущее, основанное на прошлом, вероятно быть неудачным.

Если вы делаете большие выделения в своей игре, вам нужно быть осторожным с обработкой памяти. Сборщик мусора работает на основе прогнозирования, основанного на прошлых событиях, и большие блоки памяти на 32-битной машине могут иметь разрушительные последствия для будущих распределений, если не будут должным образом управляться. Если вы этого не сделали, автоматически не предполагайте, что я неправ; если бы вы сделали это, я бы приветствовал объяснение того, как сделать это правильно (то есть, как выполнить дефрагментацию памяти, чтобы убедиться, что я всегда могу выделить 50-100 МБ памяти в данный момент времени).

...