2008-09-17 7 views
11

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

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

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

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

+0

В .Net объединение в пул полезно, когда (a) создание заданного объекта происходит медленно или (b) в 32-разрядном приложении для уменьшения фрагментации памяти массива/списка/dict и т. Д., В котором используются данные блок> 64 КБ, например > 8K элементов x 8 B на элемент, или 16K элементов x 4 B на элемент. .Net ** никогда не перемещает (перемещает) такие большие блоки **, что может привести к фрагментированной памяти, если вы каждый раз запрашиваете разные размеры. Обеспечьте выделение достаточно больших # элементов спереди и сохраните их. (Но очистите их поля, когда они не используются, поэтому GC может восстановить все, на что они указывают.) Если превышает выделенный размер, удвоьте # элемента. – ToolmakerSteve 2014-03-22 20:34:36

+0

В крайнем случае у меня есть одно 32-разрядное приложение (из-за зависимостей, которые не работают в 64-разрядной версии), поэтому, хотя я на 64-разрядной Windows с большим количеством ОЗУ, чтобы избежать фрагментации памяти, я имел для сохранения списков списков, чтобы каждый под-список соответствовал 64 КБ, так что .Net не выделял бы «кучу больших объектов». Таким образом, он может «компактно» запоминать во время GC, а не создавать большие «дыры» в адресном пространстве 4 ГБ приложения. Это конкретное приложение было проблематичным, потому что оно включало в себя много дистрибутивов DirectX из встроенной (не-НЕТ) памяти. Смешение распределений non.Net и .Net привело к фрагментации. – ToolmakerSteve 2014-03-22 20:41:13

ответ

21

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

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

+8

Это прекрасно, если вы понимаете, что GC.Collect не обеспечивает детерминированную сборку мусора. Вы сообщаете GC только о запуске своего цикла, и в этом случае определяется, следует ли собирать память. Вызов GC.Collect не гарантирует сбор памяти. – JeremiahClark 2008-10-18 03:57:33

+1

Перефразировано, чтобы показать недетерминированность. – 2009-02-09 16:49:47

+1

Помните, что ** вызов GC.Collect() слишком часто может принести больше вреда, чем пользы **: любые объекты, которые все еще имеют ссылки, будут перемещены из поколения 0 в gen 1 или из gen 1 в gen 2. Любые такие объекты будет придерживаться дольше/потребует больше работы для GC для удаления. ТОЛЬКО вызывайте GC.Collect(), когда еще НЕ обрабатываются сообщения, так что ссылки на какие-либо временные данные отсутствуют. – ToolmakerSteve 2014-03-22 20:22:41

1

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

5

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

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

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

0

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

2

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

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

3

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

1

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

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

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

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

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

7

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

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

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

11

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

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

2

Из-за звука, похоже, вы говорите о детерминированной финализации (деструкторы в 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; 
    } 
} 
2

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

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

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

5

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

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

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

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

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

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

Смежные вопросы