#.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, и он остается на этом уровне в течение нескольких дней. Итак, первый вопрос таков
- Почему он не достигает 10 ГБ?
В любом случае, я ожидаю утечек памяти, поэтому у меня есть инструмент dotnet-gcdump, установленный в этом же контейнере, поэтому я продолжаю собирать дамп для дальнейшего анализа dotnet-gcdump collect
. Как только я выполняю эту команду, я вижу, что потребление памяти запущенным контейнером падает с 7GB
до 3GB
и остается на этом уровне. .gcdump
Однако размер самого результирующего файла равен только ~200MB
тому, что в нем нет ничего подозрительного. Итак, следующие вопросы
- Как сбор дампа снижает потребление памяти? Я бы предположил, что он выполняет GC с уплотнением LOH, но в документах об этом не упоминается.
- Почему эта память не освобождается автоматически, если инструмент может это сделать?
- Почему результирующий дамп имеет размер всего 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 КБ для всего, что ниже