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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

Я нахожу выделение объектов в .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();
}

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

nodeFactory = new ListFactory<RouteNode>();

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

EDIT: из того, что я добавил в комментарии, для downvoters.

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

Правило № 2

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

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

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

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

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