Как справиться с выделением огромного объема памяти при использовании EF Core приложения ASP.NET Core 2.0?

Я наткнулся на очень странную проблему. Всякий раз, когда запускается веб-приложение, dotnet.exe имеет приличное использование памяти (около 300 МБ). Однако, когда он касается некоторых частей (я чувствую, что это связано с использованием EF Core), он выделяет огромный объем памяти за короткий промежуток времени (около 8 ГБ за 2-3 секунды).

Это использование памяти занимает около 10-15 секунд, после чего объем памяти устанавливается примерно на 600 МБ, и он работает нормально.

Я попробовал как dottrace, так и встроенные средства диагностики, чтобы понять, что выделяет столько памяти, но не нашел ничего значимого:

Dottrace для потока, потребляющего больше всего памяти, но я не смог поймать моментальный снимок памяти, будучи очень большим (он показывает мне только около ~ 1 ГБ в целом и около 800 МБ управляемой памяти).

использование памяти dottrace

Дельта VS Diagnostic Tools между исходным уровнем и сразу после всплеска памяти

Дельта Visual Studio

Подробная информация о наиболее часто используемом типе

Как я могу найти первопричину такого выделения памяти? Странно, что это не похоже на утечку, так как память со временем освобождается.

Вопрос: Как решить проблему выделения огромного объема памяти при использовании EF Core приложения ASP.NET Core 2.0?


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

Repository<T> : IRepository<T>
    <- DbContext

ScopedDataAccess : IScopedDataAccess
    <- DbContext
    <- logging service
    <- dozens of IRepository<T>

Все "ограничено":

services.AddScoped<IScopedDataAccess, ScopedDataAccess>();
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));

Я удалил около половины внедренных репозиториев в ScopedDataAccess, и требуемая память уменьшилась примерно до половины.

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

Диаграмма средств диагностики

Кроме того, я дважды проверил, что остановил все асинхронные задания (например, Quartz).


person Alexei - check Codidact    schedule 03.04.2019    source источник
comment
Что на самом деле происходит в вашем приложении? Память выделяется по мере необходимости, поэтому, если у вас нет утечки памяти, это означает, что вы делаете что-то, что на самом деле требует большого количества памяти. Это может быть получение огромного количества результатов из базы данных (то есть миллионы записей за раз), может быть загрузка больших файлов (без буферизации в файловую систему) или даже кажущиеся безобидными вещи, такие как чрезмерный выбор в запросе для таблицы, содержащей двоичные файлы. данные blob. Если вы явно не исключаете столбец BLOB-объектов, вы фактически загружаете все эти данные в память.   -  person Chris Pratt    schedule 03.04.2019
comment
Я проверил с помощью профилировщика SQL, и там практически нет активности. Я не читал никаких пользовательских файлов во время инициализации. Однако из данных трассировки похоже, что поток генерирует (испускает) некоторые метаданные, связанные с внедрением зависимостей / EF Core. В моем коде выделение не выполняется явно, потому что я могу использовать его шаг за шагом и видеть, как память выделяется чем-то вне моей базы кода.   -  person Alexei - check Codidact    schedule 03.04.2019
comment
Это не значит, что он не инициирован вашей кодовой базой. Примеры, которые я привел, были всего лишь примерами. Это, конечно, не исчерпывающий список возможных виновников. Общая идея состоит в том, что вам нужно смотреть на те области вашего кода, где вы действительно используете большие объемы памяти. Возможна любая обработка файлов. В противном случае вы просто хотите посмотреть на большие объемы выделения объектов. Чаще всего это происходит с запросами EF просто потому, что EF материализует результат в виде графа объектов, но вы также можете создавать экземпляры своих собственных больших объектов.   -  person Chris Pratt    schedule 03.04.2019
comment
@ChrisPratt - я определил конкретную службу, которая, похоже, связана с распределением памяти, и если я закомментировал все функции, но оставил одну внедренную зависимость, которая вообще не используется. Около 3 ГБ все еще выделено и в конечном итоге освобождено. Итак, я предполагаю, что это как-то связано с DI.   -  person Alexei - check Codidact    schedule 03.04.2019
comment
Ну, по сути, это не ДИ. Это может быть конкретная служба, которую вы регистрируете в контейнере DI, но проблема не в самом внедрении зависимостей. Обновите свой вопрос тем, что вы нашли (и продолжайте делать это).   -  person Chris Pratt    schedule 03.04.2019


Ответы (1)


Не полный ответ, но я сделал следующее и значительно уменьшил память (и использование ЦП):

  • упрощенный граф зависимостей за счет разделения больших сервисов, требующих множества внедренных сервисов
  • обновлен до ASP.NET Core 2.1

Последний шаг оказал наиболее заметный эффект, и мои инструменты диагностики теперь показывают гораздо более удобный график:

Снимок средств диагностики VS 2017

person Alexei - check Codidact    schedule 22.04.2019