Как стать автором
Обновить

Комментарии 43

Такие ошибки можно диагностировать, если проверять запросы, которые улетают в СУБД.
Конечно для CMS это сделать на порядок сложнее, но результат того стоит. Причем найти «плохие» запросы можно даже без большого объема данных и высокой нагрузки.
в данном случае «NewRelic» не показал нам никаких изменений в активности к СУБД.

Umbraco использует кэш для этого, следовательно никаких лишних запросов в базу.
Если данные закэшированы, от откуда такой memory pressure появляется? Или Umbraco кэширует датасеты из базы, а потом при запросе их материализует в объекты?
в умбрако в качестве кэша используется XML файл (XmlDocument). Cтроится дерево сайта из базы и сохраняется в файл. Следовательно когда нам нужет обьект Node или DynamicNode, новый инстанс создается и наполняется данными из XML файла.

Отсюда и memory pressure. Тоесть аналогично еслибы это были датасеты из базы, но в нашем случае это XML
И большой XML получился в вашем случае?
на данный момент 10 MB
Мда… при таких объемах можно было держать все дерево в памяти и вообще проблем не было бы. Странная архитектура однако.
Из разряда «я взял нож, начал резать хлеб, и порезал пальчик. Вся причина — в ноже! Он был острый!»
То есть абсолютно понятно, что виновата во всем реализация umbraco.MacroEngines.DynamicNode,
но тема статьи — «как Garbage Collector может убить производительность». GC оказывается кривой и всё погубил. Ай-ай-ай.
Назвали бы «Как по-бырому найти, кто ж тут устроил утечку памяти»…
Статья очень познавательная, но с заголовками прямо беда какая-то в последнее время.
когда искал суть проблемы я пытался найти ответ по ключевым словам связаным с 'CPU'. На тот момент я и предствить не мог что все завязано на утечках памяти.
Мои поиски привели меня на статью — http://blogs.msdn.com/b/tess/archive/2006/06/22/asp-net-case-study-high-cpu-in-gc-large-objects-and-high-allocation-rates.aspx и благодоря ей я открыл для себя то что GC тоже может быть источником проблемы.
Тоесть эта статья стала отправной точкой.

По этой же причине и решил назвать статью более приближенным к проблеме названием.
я может и ошибаюсь, но под memory leaks подразумевают утечки памяти, а не избыточную алокацию.
для избыточной алокации в том же jetbrains используют термин «memory traffic», в java я не знаю устоявшегося термина, обычно использую «избыточное выделение».

>> В .net 4.0, когда запускается сборка мусора, всем потокам GC присваивается максимальный приоритет. Это означает, что все ресурсы сервера будут направлены на сборку мусора и, помимо этого, все остальные потоки (обрабатывающие входящие запросы) будут временно приостановлены до момента, пока сборка мусора не закончится.

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

так же не понял про поколения и пример с выделением: примеры, что вы привели с выделением памяти дальше Gen0 не должны были уйти, в крайнем случае Gen1, как же оно доживало до Gen2? там же подняли, посмотрели, дерево можно освободить.
Если сборщик запускается в момент обработки дерева, все текущие объекты попадут в Gen1. Судя по графику, так и происходило — CMS поднимал весь сайт в память, в процессе памяти не хватало, запускался сборщик, и так несколько раз.
то есть после 2х сборок мы уже сразу оказываемся в gen2?
так как если судить по скринам, то в gen1 у автора ничего сильно не висит.
Похоже что метод затягивал много объектов, строил из них дерево и начинал по этому дереву бегать. Пока бегал по дереву параллельный запрос затягивал много объектов итд, в итоге они оседали в gen2 и блокирующая сборка мусора останавливала приложение.

Причем под маленькой нагрузкой отловить такое не выйдет, потому что объекты будут умирать еще на Gen0-Gen1.
то есть вроде как и параллельная, но на практике последовательная сборка? тогда не понимаю, почему сборщик в .net так люто все плюсуют, если он не обеспечивает нормальной работы без больших провалов (извиняюсь за вопрос, так как сам на java специализируюсь)

существует несколько видов сборщика и несколько их стратегий. Зависит от настроек в вебконфиге
Кончилось время редактирования. Более свежая информация тут
ну, про несколько вы погорячились: 2 вида сборщика и 2 стратегии. По ссылкам очень общая информация из разряда «мы сделали хорошо, верьте нам» =)
Ну более подробно обсуждается в книжке Pro .NET Performance, но её в беслпатном доступе нет, поэтому и ссылаться на неё я счел невозможным. Если вдруг заинтересуют внутренности дотнета — лучше книжки не найти. Тот же Рихтер очень растекается мыслью по древу и его довольно тяжело читать. А эта книжка мне понравилась намного больше — коротко и по делу.
Кстати есть и на русском, перевод относительно сносен (в отличие, кстати, от того же Рихтера). Выкладываю, потому что на русский перевели немного нерелевантно и гуглить это название может быть тяжело:
www.ozon.ru/context/detail/id/23816449/

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

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

Так вот сборка Gen2 — была изначально блокирующая, то есть останавливала все потоки. Потом придумали конкурентную сборку мусора для клиентского GC. А только в .NET 4.5 (у автора 4.0) прикрутили server background GC, который снижает паузы в разы.

Вообще автору можно было бы переключить версию .NET FW в 4.5 и сразу проблема стала бы менее заметна.

Подробности — blogs.msdn.com/b/dotnet/archive/2012/07/20/the-net-framework-4-5-includes-new-garbage-collector-enhancements-for-client-and-server-apps.aspx
задача стояла не спрятать проблему, а пофиксить проблему. Да можно было попробовать переключиться на 4.5, но это потребовало бы большего времени на тестирование всей системы. И где гарантии, что при background GC сайт выдердит желаемую нагрузку (при наличии описанной проблемы)? Да и кстати background GC только перестал бы блокировать другие потоки, при этом утилизация CPU была бы максимальной.
Я выше написал, что надо было еще при разработке посмотреть какие запросы летят в базу. Это бы исключило появление проблемы.
А что касается высокой нагрузки на CPU, то это само по себе не проблема. Проблема только тогда, когда сервер на запросы не отвечает.
тогда, не для холивара, а действительно интересно: многие дотнетчики в общении указывают, что сборщик в .net работает в разы лучше чем в java, но в разговоре оказывалось, что все примерно знали как работает в .net и никто не знал как в jvm, может вы сможете базовое сравнение привести?

p.s. да, я знаю легенду о том, что в .net сборщик сразу написали на лиспе, прогнали пачку теорий и эвристики, а уже потом переписали на «правильный» язык. но из того что вижу даже нормальный cms появился только с версии 4.5, хотя лично я считал что он имеется чуть ли не с рождения.
Как «типичный дотнетчик» я понятия не имею как работает Java GC. Но быстрое гугление говорит, что они эквивалентен .NETному. Те же поколения, параллельные и конкурентные сборки мусора итд. Только в Java настроек побольше, а в .NET сборщик, что называется self-tuning. Но кроме синтетических примеров разницу сложно будет увидеть.

Имхо основная разница не в сборке мусора, а в генерации мусора. В .NET есть структуры, которые размещаются на стеке и позволяют вообще не создавать мусор без необходимости. У Java даже Integer это ссылочный тип, размещаемый в куче. А в .NET все реализации энумераторов это структуры, и foreach (аналог for\in) не вызывает аллокаций. То есть при высокой нагрузке в .NET проще оптимизировать аллокации.

С другой стороны в JVM есть HotSpot, который оптимизирует самые медленные куски кода (возможно и за счет размещения объектов на стеке). В итоге это может дать больший прирост, чем ручная оптимизация в .NET.
Hotspot это название JVM. В jvm есть jit, но насколько я помню он и в net vm есть)
И все же в java есть примитивы. К примеру int). В java 9 обещают некий аналог struct )
постараюсь ответить сразу обоим за раз:

>> В .NET есть структуры, которые размещаются на стеке и позволяют вообще не создавать мусор без необходимости.
как позже заметили есть EscapeAnalysis который может убрать лишнюю алокацию. но да, гибкости на уровне языка у .net больше, рантайм у jvm больше работы делает, так что паритет в этом плане.

>> У Java даже Integer это ссылочный тип, размещаемый в куче
в общем случае да, но во многих местах тот же EscapeAnalysis и последующий EliminateAutoBox неплохо спасает

>> А в .NET все реализации энумераторов это структуры, и foreach (аналог for\in) не вызывает аллокаций.
энумы во многих местах это просто константы, foreach тоже после EscapeAnalysis удаляет создание итераторов, так что можно считать, что в большинстве случаем не имеем дополнительной алокации

>> С другой стороны в JVM есть HotSpot, который оптимизирует самые медленные куски кода (возможно и за счет размещения объектов на стеке)
HotSpot это всего-лишь одна из реализаций и оптимизация кода не связана с алокацией. В явном виде размещения объекта на стеке невозможно, если дергается new и конструктор, то точно хип, но если EscapeAnalysis уверен, что объект не убегат, то он может произвести взрыв объекта и от него останется лишь набор внутренних полей разбросанных по стеку и регистрам (сказать что у нас получится в конкретном куске кода я не пытаюсь, можно предположить по анализу потребления памяти или посмотрев ассемблерный код для данного метода).

>> В jvm есть jit, но насколько я помню он и в net vm есть)
есть, но в намного более простом варианте, поддерка профайлинга отсутствует, девиртуализация вызовов тоже, в итоге инлайнинг что и есть, то зачастую только некоторых невиртуальных методов (в java все методы виртуальные, поэтому девиртуализация жизненно необходима для хорошей производительности, в дотнете это не так, тут больше похоже на c++). плюсом можно назвать более быстрый прогрев и переход на машинный код, что критично для десктоп приложений.

>> В java 9 обещают некий аналог struct )
обещанные value types на практике не совсем аналог структур, так как они будут immutable, с mutable можно отгребсти пачку веселья. а вот generic на примитивных типах для локальности данных тоже интересны и уже есть частично сделаны, но это уже в 10 стоит как target.

p.s. спасибо за ссылки msdt на gc, хоть и не до конца, но примерно разобрался со сборщиком вашим, так как везде уж в слишком гуманитарном виде это описывается, в msdn хоть timeline присутствует.

p.p.s. я отношусь к тем java разработчикам, которые только рады открытию .net =) так как хоть можно будет посмотреть, как же у вас работает, а не пытаться собирать со слухов крупицы данных.
в общем случае да, но во многих местах тот же EscapeAnalysis и последующий EliminateAutoBox неплохо спасает

Как .NETчики верят, что самонастраивающийся gc работает лучше, чем вручную настраиваемый у java, так и джависты верят, что встроенные оптимизации сработают лучше, чем ручное уменьшение аллокаций.

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

в .NET есть профайлинг, но работает не так как в java. Данные о скорости загрузки\работы сборок собираются в windows и происходит пересборка скомпилированных образов приложений. А еще есть MPGO (http://blogs.msdn.com/b/dotnet/archive/2012/03/20/improving-launch-performance-for-your-desktop-applications.aspx) и Multi-core background jit (https://eknowledger.wordpress.com/2012/04/26/clr-4-5-multicore-just-in-time-jit/). Но основная цель — уменьшение времени загрузки, а не увеличение скорости работы. Низкоуровневые оптимизации отдаются на откуп к разработчикам, поэтому надобности в онлайн-профилировании нет.

Девиртуализация вызовов есть, но простейшая (ибо большинство вызовов невиртуальные), подробнее тут — www.codeproject.com/Articles/25801/JIT-Optimizations. Статья очень старая, с тех пор много улучшений было. Инлайнингом можно кстати и вручную управлять stackoverflow.com/questions/473782/inline-functions-in-c
>> так и джависты верят, что встроенные оптимизации сработают лучше, чем ручное уменьшение аллокаций

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

>> Девиртуализация вызовов есть, но простейшая

она работает только для sealed классов, так как в .net нет такого понятия как «деоптимизация метода и повторное перепрофилирование», а по поводу «большинство вызовов невиртуальные» я бы поспорил, так как имея на входе в метод интерфейс, на этапе компиляции метода при первом вызове (а именно так jit у .net работает) мы не знаем кто может туда попасть. имея профайл мы можем произвести девиртуализацию и если уже ошиблись, то тогда откатываем её и отправляем на повторное профилирование.

>> в .NET есть профайлинг, но работает не так как в java

это уже не совсем online профайлинг, а больше стандартный c/c++ подход, когда собираем статистику и на следующем запуске пытаемся её использовать. Но с данным подходом есть проблема: например у нас рестарты бывают только с выходом новой версии, в итоге собранный профиль является уже устаревшим.

>> MPGO

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

>> Multi-core background jit

ну иметь один блокирующий jit поток на многоядерной архитектуре само по себе нонсенс

>> Но основная цель — уменьшение времени загрузки, а не увеличение скорости работы

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

p.s. люблю конструктивные споры, столько линков новых получаешь =)
по поводу «большинство вызовов невиртуальные» я бы поспорил

С чем бы поспорили? Я просто по проектам мерил, когда оптимизацией занимался. interface calls + virtual calls — не более 20%, при этом очень малая часть приходится на места, которые надо оптимизировать. Так что в .NET реальная полезность подобной оптимизации минимальна.

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

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

Для высоконагруженных, но не lowlatency, систем между 10% производительности от инлайнинга и увеличением сборки мусора в 2 раза я выберу производительность в случае если эти сборки случаются раз в минуту и длятся 1с, так как на паузе я потеряю лишнюю секунду, но за счет лучшего кода выиграю 6, итого суммарный счет 5с профита. Именно поэтому многие для lowlatency систем на средних размерах хипа в jvm продолжают использовать CMS и не переключаются на G1, так как уровень оптимизации кода и инлайнинг у cms на данный момент выше, так как нету дополнительных барьеров памяти необходимых для гарантии корректности при паралельной сборке регионов.

А данный пост показывает лишь то, что перед выпуском приложения не проводилось какое-либо нагрузочное тестирование, так как 30 сесий способные положить сайт с хипом свыше гига это немного странно. Большие паузы на gc и suspend всех потоков не показывают ничего, кроме того, что сборка мусора в версии .net у автора не умеет работать в фоне, а для сборки стопает все (ниже уже был совет обновить версию рантайма, но текущее поведение вызывает лишь улыбку).

«порог — 80%, продолжительностью — 15 секунд» — извиняюсь, но 15 секунд собирать мусор это МНОГО, что еще раз говорит, о плохом gc, автор статьи же говорит о «не более 1 минуты, но с очень большой частотой». в данном случае конечно ваши слова про алокацию имеют смысл, так как любая сборка это неплохой такой downtime, до этого я считал что даже 10GB heap вы собираете в течении 5-10c, что тоже много, но не так критично.

p.s. почему я говорю много (если говорить в терминах .net): наше приложение с gen0 поколением в 6G суммарно паузы меньше 0.140c раз в 20-30с и gen2 в 20+G (с учетом gen0 и gen1 суммарно 32GB) меньше 1с и вызовом 2 раза в сутки. онлайн 2к+ пользователей и данные паузы нам и то не очень нравятся, но пока это менее критичная проблема чем функционал =) поэтому читая про минуту и не видя удивления у окружающих у меня и возник небольшой диссонанс.
10% производительности от инлайнинга
Для большинства приложений это нереально. Инлайнинг спасает в так называемых тесных циклах, когда кроме вызова функции в теле цикла почти ничего нет, а в самом теле функци выполняются только вычислительные операции.

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

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

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

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

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

«порог — 80%, продолжительностью — 15 секунд» — извиняюсь, но 15 секунд собирать мусор это МНОГО, что еще раз говорит, о плохом gc
Во-первых 15 секунд это не время GC, а время, когда приложение работает, а не ждет ответа базы, что вообще не говорит о работе GC. Во вторых gc тут совершенно не при чем. Попробуйте в любом приложении на любом языке в секунду выделять по 500 мб, будет тот же эффект. Даже в языке без GC.

наше приложение с gen0 поколением в 6G суммарно паузы меньше 0.140c раз в 20-30с и gen2 в 20+G (с учетом gen0 и gen1 суммарно 32GB)

При таких объемах у ТС вообще не было бы проблем. У него весь объем памяти меньше вашего Gen0.
>> Во-первых 15 секунд это не время GC, а время, когда приложение работает

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

>> Во вторых gc тут совершенно не при чем.

позволю себе процитировать:
А именно:
* На момент сборки дампов Garbage Collector был запущен (поначалу я не придал этому внимания);
* Очень большой размер кучи;
* Все 4 потока принадлежат Garbage Collector и съедают 100% CPU.

что позволяет сделать вывод, что уже к этому моменту 15с находимся в gc

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

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

>> всегда занимался уменьшением аллокаций, это позволяло в разы ускорить приложение

что еще раз указывает, на узкое место в виде gc, n% еще бывыло, но редко когда это число достигало двухзначных цифр, трехзначных не припомню вообще.

>> При таких объемах у ТС вообще не было бы проблем. У него весь объем памяти меньше вашего Gen0.

и как бы это ему помогло? собирать больший объем памяти? расшифруйте пожалуйста.

>> Попробуйте в любом приложении на любом языке в секунду выделять по 500 мб, будет тот же эффект. Даже в языке без GC.

intellij idea, есть пару багов, алокация за минуту 80-120ГБ памяти (размер в секунду свыше гигабайта), за минуту суммарное время в gc 6s, основное время на копирование кусков памяти с одной точки в другу (просадка на memcpy)
уточню, время смотрелось по профайлеру, где мы находились в тот момент.
отдельно метрики снимались сколько провели в gc, так как видно что тригерился он очень часто, пускай и на мелкие промежутки в 3-4ms.
один из багов конкатенация строк в цикле (oldString = oldString + suffixN), так как строки immutable, и ближе к концу цикла достигали 80+ мебабайт, то только и делали что ждали копирования.
но даже в этом случае gc пыхтел, но справлялся
туда же: обратите внимание на снимок, в gen2 данных немного, в gen1 еще меньше, основные объемы в gen0 которые только что выделились и их сейчас собирают.

Возникает вопрос: почему сборка проходит недостаточно быстро?

p.s. раз уже затронули тему latency, то сколько примерно времени уходит на сборку хипа в 10GB, так как .net под рукой нету, а мерять в virtualbox это немного не то, то хотелось бы получить данные близкие к первоисточнику.
что позволяет сделать вывод, что уже к этому моменту 15с находимся в gc

Не позволяет. Картинка выше процитированного сообщения показывает что поток GC работает менее 30% времени. Но учитывая что GC останавливает остальные потоки, вполне может получиться огромная суммарная пауза в работе.

что еще раз указывает, на узкое место в виде gc

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

и как бы это ему помогло? собирать больший объем памяти? расшифруйте пожалуйста.

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

intellij idea, есть пару багов, алокация за минуту 80-120ГБ памяти (размер в секунду свыше гигабайта), за минуту суммарное время в gc 6s, основное время на копирование кусков памяти с одной точки в другу (просадка на memcpy)
Это ни о чем не говорит. Тут зависит от того сколько потоков выделяют память, блокирует ли GC остальные потоки. Если приложение десктопное, то вполне может быть что вся эта операция происходит в одном потоке. Выделил 1гб, GC отработал за 500мс, потом еще выделил итд. У автора другой случай: пока GC чистит выделенную память в одном потоке другой успевает выделить еще. При этом память не освобождается, ибо много живых ссылок присутствует на момент начала сборки.
Многие дотнетчики верят другим дотнетчикам, которые не любят java.) Сколько не пишу на java, проблем со сборкой мусора никогда не было, все быстро и четко. А если и возникнут, всегда можно потюнить.
C# этим страдает, без GC.Collect() память по 5 минут может не освобождаться.
Примерно 2 недели назад наш мониторинг тул (NewRelic) начал детектить большое количество падений сайта продолжительностью не более 1 минуты, но с очень большой периодичностью.

Вы наверно хотели сказать «с очень большой частотой»?
спасибо, поправил. именно это и имелось в виду.
Скажите, а как вам вообще релик? на каком плане сидите? Как давно используете? Были ли с ним какие-то проблемы?
Прекрастный tool. Используем около года базовую (бесплатную) версию. Проблем особо небыло. Один раз в самом NewRelic была проблема с производительностью. Их профайлер убивал CPU. Нашли проблему все тем — же Debug Diagnostics Tool. Зарепортили багу, обновились и все работает в обычном режиме.

А вообще самое сложное — научиться понимать/читать иформацию NewRelic.
10 мб для 1 юзера?
Жестоко.

А не пробовали параллельный gc? Насчет .net не знаю, но в java он есть.
В .Net он есть и использование его для серверных приложений просто обязательно. Я не в курсе, как с этим дела в вебе, но в standalone .Net сервере у меня с каждой версией .Net начиная с 1.1 была новая сказка с GC — то он блокировал произвольные потоки, хотя и был в параллельном режиме, то зависал Finalizer Thread, то не отдавал поток в ThreadPool и тот оставался навсегда. Ближе к 4.0 уже стало все относительно хорошо, а в 4.5 мои проблемы закончились, да и сервер перестали использовать. :)
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории