Как избежать сбора мусора в режиме реального времени.СЕТЕВОЕ приложение?

StackOverflow https://stackoverflow.com/questions/85283

Вопрос

Я пишу финансовое приложение на C #, которое получает сообщения из сети, переводит их в другой объект в соответствии с типом сообщения и, наконец, применяет к ним бизнес-логику приложения.

Дело в том, что после применения бизнес-логики я совершенно уверен, что этот экземпляр мне больше никогда не понадобится.Вместо того чтобы ждать, пока сборщик мусора освободит их, я бы хотел явно "удалить" их.

Есть ли лучший способ сделать это в C #, должен ли я использовать пул объектов для повторного использования всегда одного и того же набора экземпляров или есть лучшая стратегия.

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

Это было полезно?

Решение

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

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

Другие советы

Посмотри сюда: http://msdn.microsoft.com/en-us/library/bb384202.aspx

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

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

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

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

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

Следует отметить , что сборщик мусора отличается на разных устройствах .NET frameworks - на compact framework (который работает на Xbox 360 и мобильных платформах) это GC, не относящийся к поколению, и поэтому вы должны быть гораздо более осторожны с тем, какой мусор генерирует ваша программа.

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

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

Наконец, вы уверены, что GC вызывает у вас проблему?Вероятно, вам следует измерить и доказать это, прежде чем внедрять какие-либо решения для экономии производительности - вы можете навлечь на себя ненужную работу!

"Цель состоит в том, чтобы избежать сбора мусора для использования любого процессора во время критичного по времени процесса"

Q: Если под критическим временем вы подразумеваете, что слушаете какое-то эзотерическое устройство и не можете позволить себе пропустить прерывание?

A: Если это так, то C # - не тот язык, который нужно использовать, для этого вам нужен ассемблер, C или C ++.

Q: Если под критическим временем вы подразумеваете время, когда в канале много сообщений, и вы не хотите, чтобы сборщик мусора замедлял работу?

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

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

Получите хорошее представление о том, как ведет себя сборщик мусора, и вы поймете, почему то, о чем вы здесь думаете, не рекомендуется.если только вам действительно не нравится, что среда CLR тратит время на перестановку объектов в памяти много.

Насколько интенсивно работает приложение?Я написал приложение, которое записывает 3 звуковые карты (управляемые DirectX, 44,1 кГц, стерео, 16-разрядные) блоками по 8 КБ и отправляет 2 из 3 потоков на другой компьютер через TCP / IP.Пользовательский интерфейс отображает измеритель уровня звука и (плавную) прокрутку заголовка / исполнителя для каждого из 3 каналов.Это работает на ПК с XP, частотой 1,8 ГГц, 512 МБ и т.д.Приложение использует около 5% процессора.

Я воздержался от ручного вызова методов GC.Но мне действительно пришлось настроить несколько расточительных вещей.Я использовал Ant profiler от RedGate, чтобы отточить расточительные части.Потрясающий инструмент!

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

Если это абсолютно критично по времени, то вам следует использовать детерминированную платформу, такую как C / C ++.Даже вызов GC.Collect() будет генерировать циклы процессора.

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

Судя по всему, кажется, что вы говорите о детерминированной финализации (деструкторы в C ++), которой не существует в C #.Самое близкое, что вы найдете в C #, - это Одноразовый шаблон.По сути, вы реализуете IDisposable ( доступный для идентификации) интерфейс.

Основная схема такова:

public class MyClass: IDisposable
{
    private bool _disposed;

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

    protected virtual void Dispose( bool disposing )
    {
        if( _disposed )    
            return;

        if( disposing )
        {
            // Dispose managed resources here
        }

        _disposed = true;
    }
}

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

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

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

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

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

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pooling-and.aspx

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top