dotnet-неожиданный размер и влияние дампа gcdump

#.net-core #memory-leaks #garbage-collection

#.net-core #утечки памяти #сбор мусора

Вопрос:

Я использую простое приложение CRUD, созданное с помощью ASP.NET Core и EF Core 3.1 в кластере docker swarm в ubuntu. Я использую только управляемый код. В контейнере 10GB указан лимит памяти. Я могу проверить работающий контейнер и убедиться, что это ограничение действительно установлено, я также вижу, что DOTNET_RUNNING_IN_CONTAINER оно установлено true равным . При запуске приложения потребление памяти составляет около 700MB , и оно медленно накапливается. Как только он достигнет 7GB (я вижу это в общей статистике контейнера) Я начинаю получать OutOfMemoryException s, и он остается на этом уровне в течение нескольких дней. Итак, первый вопрос таков

  1. Почему он не достигает 10 ГБ?

В любом случае, я ожидаю утечек памяти, поэтому у меня есть инструмент dotnet-gcdump, установленный в этом же контейнере, поэтому я продолжаю собирать дамп для дальнейшего анализа dotnet-gcdump collect . Как только я выполняю эту команду, я вижу, что потребление памяти запущенным контейнером падает с 7GB до 3GB и остается на этом уровне. .gcdump Однако размер самого результирующего файла равен только ~200MB тому, что в нем нет ничего подозрительного. Итак, следующие вопросы

  1. Как сбор дампа снижает потребление памяти? Я бы предположил, что он выполняет GC с уплотнением LOH, но в документах об этом не упоминается.
  2. Почему эта память не освобождается автоматически, если инструмент может это сделать?
  3. Почему результирующий дамп имеет размер всего 200 МБ?

Ответ №1:

Как объясняется в документации gcdump: «дампы GC создаются путем запуска GC в целевом процессе, включения специальных событий и регенерации графика корней объектов из потока событий».

Таким образом, он напрямую отвечает на ваш вопрос 2 — он запускает полный GC, который может уплотняться, а может и не уплотняться, но он наверняка собирает gen2. Это также отвечает на вопрос 4 — это не «дамп памяти», а особый вид диагностических данных о графе объектов (зависимости и имена типов), без самих данных.

И что касается вопросов 1 и 3 — это пример того, что GC является «недостаточно агрессивным». Это своего рода проблема «жизни на грани», когда процесс почти соответствует ограничениям контейнеров, и GC иногда не может его интерпретировать. Другими словами, он думает, что у него достаточно места, но это не так. Пожалуйста, имейте в виду, что это чрезмерное упрощение. В таком случае полная GCs может не произойти или произойти слишком поздно. Я бы подтвердил это, наблюдая за процессом dotnet-trace с gc-collect помощью профиля with.

В качестве решения рассмотрите возможность установки лимита вручную, с помощью GCHeapHardLimit , на какое-то явно меньшее значение, например 8 ГБ.

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

1. спасибо, я проверил gcdump, к сожалению, нет способа увидеть сумму всех значений для каждого столбца, но похоже, что сумма будет составлять не более 50 МБ, даже если отключен «только мой код», как мне прочитать и использовать эти данные?

2. Как вы его исследовали? AFAIK, вы должны иметь возможность использовать Visual Studio или использовать PerfView, если нет.

3. Я использовал Visual Studio, «столбец размера» составлял около 1 МБ для 5 самых больших элементов и менее 100 КБ для всего, что ниже