Aspnetcore: Asp.net Core не собирает мусор

Созданный на 21 мар. 2017  ·  136Комментарии  ·  Источник: dotnet/aspnetcore

Я не могу понять, почему ядро ​​Asp.net не собирает мусор. На прошлой неделе я позволил веб-службе работать несколько дней, и использование моей памяти достигло 20 ГБ. GC не работает должным образом. Поэтому, чтобы проверить это, я написал очень простой веб-метод, который возвращает большой набор строк. Приложение начинало с использования только 124 МБ, но с каждым вызовом веб-метода использование памяти становилось все выше и выше, пока не достигло 411 МБ. Он был бы выше, если бы я продолжал вызывать веб-метод. Но я решил прекратить испытания.

Мой тестовый код был таким:

`

[HttpGet]
общедоступная асинхронная задача> ТестГХ() {
константное строковое сообщение = "ТЕСТ";
вернуть Enumerable.Repeat (сообщение, 100000000);
}
`

Хотя я могу что-то упустить... Насколько я понимаю, использование памяти не должно увеличиваться с каждым вызовом этого метода. После того, как объект создан и отправлен в UI, память должна быть освобождена.

Как видно из скриншота ниже, даже после вызова GC память не освободилась.
netcorescreenshot

Спасибо за помощь!
Джей

investigate

Самый полезный комментарий

Есть новости по этому поводу? Я использую Core 2 поверх Net Framework, и это все еще происходит. Каждый вызов _Controller_ увеличивает используемую память, но она никогда не уменьшается. (_Я использовал шаблон WebApi_)

Все 136 Комментарий

@rynowak

Хотя я могу что-то упустить... Насколько я понимаю, использование памяти не должно увеличиваться с каждым вызовом этого метода. После того, как объект создан и отправлен в UI, память должна быть освобождена.

Куча больших объектов, вероятно, кусает вас здесь. Если вы выделяете объекты размером > 85 КБ, они будут помещаться в LOH и очень редко уплотняться. Подробнее см. http://stackoverflow.com/questions/8951836/why-large-object-heap-and-why-do-we-care (или https://github.com/dotnet/coreclr/blob/master). /Documentation/botr/garbage-collection.md#design-of-allocator, если хотите углубиться).

Я не могу понять, почему ядро ​​Asp.net не собирает мусор. На прошлой неделе я позволил веб-службе работать несколько дней, и использование моей памяти достигло 20 ГБ.

Что делает ваша служба, что создает такие большие объекты? Вам следует попробовать сделать дамп памяти до того, как он станет слишком большим, это ясно покажет вам, какие объекты остаются и почему они удерживаются (вы можете использовать визуальную студию для просмотра дампов или более продвинутый инструмент, такой как windbg или perfview).

Попробуйте выделить массив с самого начала, а не вызывать Enumerable.Repeat
или сжать память с помощью GCSettings.LargeObjectHeapCompactionMode (поддерживается в .Net Standard)

Спасибо @davidfowl и @MhAllan за ответы. Но этот пример был надуманным. Я просто хотел что-то, что бы использовало заметный объем памяти, чтобы я мог сделать снимок экрана. Правда в том, что это происходит с любым приложением, независимо от размера рассматриваемого объекта. И чтобы ответить на ваш вопрос @davidfowl , моя служба просто извлекала некоторые данные из базы данных с помощью dapper и возвращала результирующий объект. Это была одна строка данных для каждого вызова. Так что потребовалось несколько дней, чтобы память выросла до этого объема. На самом деле я пытался протестировать БД, когда наткнулся на эту особенность. Я написал небольшое консольное приложение, которое снова и снова вызывало метод.

@zorthgo уверен, что это не Dapper? Если вы создаете свои сценарии, вводя параметры непосредственно в свои сценарии SQL, как в PHP, вы получите множество кэшированных сценариев SQL. Вот как это делает Даппер
https://github.com/StackExchange/Dapper/blob/fe5c270aceab362c936456087a830e6fe1603cac/Dapper/SqlMapper.cs
Вы должны использовать профилировщик памяти, чтобы узнать, что хранит ссылки на выделенную память. Visual Studio 2017 должна помочь вам просто сделать несколько снимков из памяти до и после нескольких вызовов вашего приложения и сравнить их.

@zorthgo Да, я тоже это видел. Я использовал сервисный стек в своем консольном приложении .net core, и каждый раз, когда я звонил в API, использование памяти увеличивалось на 50 МБ. Я предположил, что это ошибка VS2017, но затем подтвердил высокую загрузку в диспетчере задач. Как заявил zorthgo, просто выполняя простые вызовы API, использование памяти значительно увеличивается и, похоже, не освобождает память.

Это происходит только в ASP.NET Core в .NET Core или это также проблема с ASP.NET Core в .NET Framework?

Можете ли вы написать аналогичное приложение с использованием MVC 5 (в System.Web) и убедиться, что вы не видите такого же поведения?

Я не могу добиться какого-либо прогресса в решении этой проблемы в ее нынешнем состоянии.

В моем случае я использовал только целевую платформу .NetCoreApp 1.1, а мое консольное приложение ссылалось на объектную модель в общем проекте.

Не уверен, что это кому-нибудь поможет. пример приложения, которое вызывает hellorequest. В этом приложении загрузочная память составляет 85 МБ, затем по повторяющемуся запросу мне удалось увеличить использование памяти примерно до 145 МБ. Иногда он падает до 125 МБ, но затем остается там. Не уверен, что это нормальное поведение, поскольку я не привык к консольным приложениям .Net Core. я всегда предполагал, что делаю что-то не так или неправильно создаю экземпляры.

https://drive.google.com/open?id=0B0Gm-m-z_U84TU5keWFTMzc1ZWc

Столкнулся с той же проблемой здесь, в приложении Asp.Net Core, развернутом в производство с 3000-5000 активных пользователей. Память на сервере вчера увеличилась до 15 ГБ. попробуй разобраться в чем дело.

Кто-нибудь делал дамп памяти и смотрел, что занимает всю память в ваших конкретных приложениях?

@davidfowl @Пинокс
Я работаю в большой компании, и мы хотим начать новый проект с ASP.NET Core, но когда я увидел эту проблему, я испугался :worried: . это критическая проблема, которая может заблокировать жизненный цикл нашего проекта.

Итак, пожалуйста, связано ли это с ASP.NET Core или .NET Core (CoreCLR)? мы будем ориентироваться на Full .NET (4.6), поэтому я и спрашиваю.

@ikourfaln в моем случае я использовал консольное приложение .net core, сервисный стек (версия .net core) и пустельгу. Странно то, что использование памяти увеличивается до уровня , затем оно внезапно останавливается и больше не увеличивается. Я думаю, лучше всего протестировать его на своей стороне с небольшой выборкой и проверить поведение.

Возможно , @zorthgo может проверить на своей стороне, видит ли он подобное поведение в том, что память используется до определенного уровня, а затем перестает увеличиваться, поскольку это поведение, которое я вижу. Я обновил свой образец приложения, включив в него пример @zorthgo, и не вижу, чтобы память убегала. Он поднимается, но в конце концов останавливается.

Я немного изменил источник:

публичный объект Любой (запрос TestGC)
{
константное строковое сообщение = "ТЕСТ";
вернуть Enumerable.Repeat (сообщение, 100000);
}

@Пинокс
Спасибо, проверю поведение на своей стороне.

Как насчет этой ошибки в 2.0?

Есть новости по этому поводу? Я использую Core 2 поверх Net Framework, и это все еще происходит. Каждый вызов _Controller_ увеличивает используемую память, но она никогда не уменьшается. (_Я использовал шаблон WebApi_)

Привет,

У меня такая же проблема с ASP.NET Core 2. Я сделал дамп памяти и попытался проанализировать. Из того, что я вижу, проблема именно такая, как сказал ОП. Мое приложение начинается с выделения около 75 МБ и очень быстро достигает ~ 750 МБ, из которых 608 МБ - это «Неиспользуемая память, выделенная для .NET».

Первый снимок при запуске приложения:
image

Второй снимок через 3 минуты и 100 запросов:
image

мы также сталкиваемся с той же проблемой, наш контроллер имеет дело с большим объемом данных (что является плохим дизайном и скоро будет заменено), каждый вызов этого контроллера вызывает рост памяти. Память уменьшается, но только на 40-50% (выигрыш 50 Мб, уменьшение 30-35 Мб), каждый вызов увеличивает память в пределах 10-15 Мб каждый раз. Служба размещается внутри структуры службы.

Похоже, у меня есть аналогичная проблема в нашей производственной службе (20-100 запросов / с), используя комбинацию:

  • Ядро ASP.NET 2.0.4
  • ServiceStack.Core 1.0.44
  • СкиаШарп 1.59.2
  • Docker v17.09.0-ce, сборка afdb6d4 на Ubuntu 16.04 x64
  • x4 сервера @ 40 процессоров, 128 ГБ памяти
  • GC сервера верно
  • каждый контейнер Docker настроен на 12 тыс. (МГц) долей процессора, 8 ГБ оперативной памяти

Приложение имеет интерфейсный веб-сервер и рабочий процесс (показаны соответственно на графиках ниже).

веб-сервер (последние 6 часов)
screen shot 2018-01-13 at 1 06 24 pm

рабочий (последние 6 часов)
screen shot 2018-01-13 at 1 07 55 pm

Оба они используют большие массивы байтов, поскольку служба действует как прокси-сервер хранилища объектов и, следовательно, помещает объекты в LOH. Мой вопрос: является ли это известным ограничением .NET Core на данный момент? Кажется, что LOH никогда полностью не очищался и не фрагментировался.

Сказав это, SOH, похоже, работает нормально, так как типичные объекты веб-API очищаются. Какие-либо предложения? Есть ли проблема с моей настройкой? Я проанализировал код и не нашел каких-либо вопиющих утечек памяти, и я не использую ничего особенного, кроме библиотеки ServiceStack.

@sebastienros - есть мысли по этому поводу? Наблюдали ли мы подобное поведение в наших системах?

  • Мы измеряем это только на 2.1, я постараюсь добавить то же самое для 2.0.
  • Все комментарии, по-видимому, связаны с упомянутой проблемой LOH, которую следует учитывать в приложении и как можно больше объединять большие массивы.

@sebastienros , несколько вопросов:

  1. Я использовал профилировщик Ants для измерения использования памяти, согласно ему фрагментация LOH не обнаружена. Можете ли вы посоветовать, как я могу проверить, страдает ли мое приложение от проблем с фрагментацией LOH?
  2. Каковы результаты на .net core 2.1? Решена ли проблема, потому что Kestrel использует Span?
  3. Что делать, если мы не можем объединить массивы — можете ли вы предложить обходной путь? Должны ли мы использовать GCSettings.LargeObjectHeapCompactionMode.CompactOnce?

Каковы результаты на .net core 2.1? Проблема решена, потому что Kestrel использует Span?

Мы лично не видели никаких доказательств того, что проблема в Kestrel. Это все еще похоже на проблему с приложением.

Что делать, если мы не можем объединить массивы — можете ли вы предложить обходной путь? Должны ли мы использовать GCSettings.LargeObjectHeapCompactionMode.CompactOnce?

@jkotas @Maoni0 Есть совет?

как я могу исследовать, если у меня такая же проблема? LOH в соответствии с профилировщиком памяти redgate почти пуст, как описывает @sinapis , но все еще использует более 1 ГБ только для одного пользователя.

Соберите трассировку и проанализируйте ее с помощью perfview. Существует ряд руководств Вэнса и других о том, как отслеживать утечки памяти .NET: https://www.bing.com/search?q=.NET%20memory%20leak%20perfview .

https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md содержит специальные инструкции для Linux по сбору трассировок.

Если вы считаете, что утечки памяти нет, а GC просто хранит больше памяти, чем вам хотелось бы, вы можете попробовать:

  • Использование объекта задания Windows или ограничения памяти контейнера докеров, чтобы ограничить объем памяти, который может использовать процесс. Сборщик мусора учитывает эти ограничения и работает более агрессивно, когда приближается к ним.
  • Или переключиться с Server GC на Workstation GC. Серверный GC нередко имеет гораздо более высокий пиковый рабочий набор. Рабочая станция имеет более низкий пиковый рабочий набор, но и более низкую пропускную способность.

Привет,

Я думаю, что у меня такая же проблема с .Net Core Web API в производстве.

Приложение работает на Windows Server 2016 с .Net Core 2.0.3. Машина представляет собой виртуальную машину Hyper-V с 28 ядрами ЦП и 24 ГБ ОЗУ. Если мы не будем достаточно часто перерабатывать пул приложений IIS, в конечном итоге мы будем использовать всю доступную память. Когда приложение начинает использовать много памяти (>=95% от общего объема системной памяти), загрузка ЦП также сильно увеличивается (иногда с 2% до 70%). Я не уверен, вызвано ли исключение OOM или нет, мы всегда перезапускаем пул до того, как это произойдет (максимальное использование памяти, которое я видел, составляло 98% памяти, используемой dotnet.exe).

Анализ дампа производственной памяти с помощью ".Net Memory Porfiler" (SciTech Software) вот что я нашел:
image

Если этот анализ верен, около 95% памяти находится в «служебных > неиспользуемых». Вот как этот редактор профилировщика памяти описывает эту категорию (на своем форуме):
_"Overhead->Unused" — это память, выделенная средой выполнения .NET для управляемой кучи. В настоящее время он не используется, но доступен для будущих распределений экземпляров. Существует множество правил, которые среда выполнения использует, чтобы решить, оставить ли выделенную память или освободить ее для ОС. Это зависит от таких факторов, как доступная память, шаблоны распределения, количество процессоров, используется ли серверный GC и т. д._

@jkotas Я применю ваши рекомендации (объект задания Windows и переключение на рабочую станцию ​​GC) и сообщу вам о результате. Пожалуйста, дайте мне знать, могу ли я извлечь какую-либо другую полезную информацию из имеющихся у меня производственных дампов памяти.

Спасибо

@sinapis @ronald7
Может ли кто-нибудь из вас поделиться приложением, которое показывает проблему? Если бы я мог воспроизвести это, мы смогли бы найти причину или, по крайней мере, удалить часть кода по частям и изолировать минимальное воспроизведение.

@sebastienros Я не могу поделиться приложением, но могу поделиться сеансом из сеанса PerfView + дамп памяти .
Некоторое описание: у меня есть веб-API ASP.NET Core 2, я создал нагрузочный тест 200 пользователей, которые отправляют один и тот же запрос в течение 10 секунд. Всего было обработано 775 запросов.

Это приложение увеличило использование памяти почти до 1 ГБ в диспетчере задач и осталось таким. Глядя на дамп, я могу насчитать около 18 МБ:

image

Так вот вопросы куда делся почти 1 Гб?

@sinapis Спасибо

Поведение, которое вы описываете, не является неожиданным, GC выделяет некоторую память по мере необходимости при пиковой нагрузке и просто освобождает ее с течением времени. Это режим GC Server, и обычно ждут периоды простоя, чтобы выпустить его и не повлиять на производительность вашего приложения. Объем памяти, который он будет резервировать, зависит от общего объема памяти, доступной в системе.

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

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

Также вы можете делать дампы во время и по окончании работы, чтобы мы могли видеть детали.

Привет @sebastienros

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

Я обновил режим GC с _server_ на _workstation_ на одном рабочем сервере, я дам вам знать через несколько часов, если это что-то изменит в использовании памяти.

Я также провел еще один тест: мы запускаем наше приложение за балансировщиком нагрузки на 4 виртуальных машинах. После удаления одной из машин из пула балансировщика нагрузки объем используемой dotnet.exe памяти не уменьшился и оставался на том же уровне даже через 30 минут. (Однако приложение по-прежнему обрабатывало несколько запросов: один запрос, отправляемый SCOM на фиктивную конечную точку каждые 30 секунд). Память не освобождалась и не возвращалась в систему.

Спасибо

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

@Maoni0, пожалуйста, поделитесь, как мне включить ведение журнала GC
Если есть какие-то другие данные, которые вы хотели бы, чтобы я предоставил, чтобы опровергнуть ошибку учета, пожалуйста, дайте мне знать, что я должен предоставить вам и как (может быть, попросить perfview собрать больше данных?)
Я попробую создать минимальный репро, но не уверен, что у меня получится, так как я не знаю, в чем проблема.

@sebastienros, надеюсь, сегодня я предоставлю еще один дамп с большим потреблением памяти.

Привет @sebastienros @Maoni0 ,

Я запускал наше приложение в режиме рабочей станции GC в течение 12 часов, но результат тот же. Я также перекомпилировал приложение с .Net 2.1 Preview 2 на одном производственном узле в течение 1 часа, я сообщу вам результат, но на данный момент процесс уже использует 2 ГБ+ ОЗУ.

image

У меня есть PerfView, работающий на этой же машине, и я собираю дампы GC, есть ли адрес электронной почты, на который я мог бы отправить вам ссылку OneDrive, к сожалению, я не могу поделиться ею напрямую в этой теме.

Если это может помочь, я также могу собрать больше метрик или журналов GC. Спасибо

@ronald7 _redacted_ Я могу переслать @Maoni0

Привет @sebastienros @Maoni0 Я только что отправил вам электронное письмо с двумя gcdump PerfView и файлом VMMap, надеюсь, это поможет. Со своей стороны, я все еще пытаюсь воспроизвести это поведение с высоким использованием памяти с помощью фиктивного приложения.

Спасибо!

Я также испытываю ту же проблему. Сбор мусора никогда не происходит! На снимке экрана показано использование памяти после выполнения около 50 запросов с использованием довольно простого приложения веб-API dotnet core.

memory-profile2

Я только что обновил приложение ASP.NET Core, работающее в Ubuntu 16.04, с 1.1 до 2.0 и столкнулся с этой проблемой. Это довольно серьезно, из-за чего ядро ​​​​часто убивает приложение из-за ошибок OOM, и я подумываю, не вернуться ли к 1.x. Есть определенные страницы, которые мы вообще не можем загрузить — даже после перезапуска Kestrel приложение сразу же исчерпывает доступную память после одного запроса! Я думал об обновлении сервера, но, основываясь на комментариях здесь о приложениях ASP.NET Core, использующих всю доступную память, я не надеюсь, что это поможет. Наш стек в основном представляет собой ASP.NET MVC Core + EF Core... ничего особенного. Если у меня будет время, я попытаюсь создать образец, чтобы воспроизвести проблему — я не думаю, что это должно быть так сложно, учитывая простоту нашего стека.

FWIW, система, которую я обновил, также имеет консольное приложение .NET Core, и у нее, похоже, нет проблем с памятью после обновления 2.0, так что это определенно проблема, связанная с ASP.NET Core.

@danports Вы пытались вызвать GC.Collect(), чтобы посмотреть, резко ли снизится использование памяти? это дало бы нам ключ к разгадке, с чего нам следует начать. если GC.Collect() (или последовательность GC.Collect/GC.WaitingForPendingFinalizers/GC.Collect) не может резко сократить использование памяти, это означает, что просто так много памяти должно быть живым, поэтому GC не может ее восстановить.

@ Maoni0 Я еще не пробовал. Я не думаю, что моя проблема связана с GC, потому что время от времени я наблюдал падение использования памяти — просто кажется, что мои приложения .NET Core 2.0 потребляют примерно в 2-3 раза больше памяти, чем когда они работали на . NET Core 1.1. 😞

Сейчас я вернулся к .NET Core 1.1 и вернусь к этому позже, когда у меня будет больше времени, возможно, после выпуска .NET Core 2.1. (Я столкнулся с кучей проблем с 2.0, и это была лишь одна из них.)

GC.Collect() не помогает. Пробовал очень простой веб-API ASP.NET Core 2.0 и 2.1 с одним контроллером, который возвращает словарь из 200 тыс. целых чисел. Выделенная память продолжает увеличиваться с каждым запросом, хотя приложение больше не использует память.

@Serjster, возвращающий 200 тысяч целых чисел (4 байта), займет 800 КБ. В этом случае вы сталкиваетесь с проблемой, описанной в этом комментарии: https://github.com/aspnet/Home/issues/1976#issuecomment -289336916.

В этом случае вы должны использовать пул массивов, чтобы повторно использовать их в запросах.

Также полезно знать, что если код работает в 64-битном режиме, то массивы/списки и т. Д., Которые содержат указатели, в два раза больше по сравнению с 32-битным. Если я правильно помню, полная структура запускает любой 32-битный код процессора в 64-битной ОС по умолчанию. Таким образом, люди, переносящие свой код, могут случайно столкнуться с проблемами LOH.

Я работаю с @Serjster , и вот что я нашел. Если я создаю проект vanilla web api с использованием ядра asp.net (я использовал 2.1 в моем последнем тесте), я замечаю, что когда я запускаю диагностический инструмент (или даже проверяю рабочую память процесса, установленную в коде), количество возвращаемых байтов продолжает подниматься, когда я достиг конечной точки. Например, если у меня есть одна конечная точка веб-API, возвращающая словарьс 20 000 элементов в нем происходит следующее:

  1. При первом обращении к методу контроллера объем памяти процесса составляет 83 МБ.
  2. Я жду несколько секунд, а затем при втором посещении он достигает 86 МБ.
  3. Я жду несколько секунд, и третье посещение перемещается на 90 МБ.
  4. Опять же - 94Мб.
  5. Я делаю это n раз, и в итоге получается около 304 МБ. Как только он это делает, он выравнивается.

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

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

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

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

РЕДАКТИРОВАТЬ - re комментарий @davidfowl : Что касается вашего комментария о редком сборе вещей ... это может иметь смысл. Но сколько времени это обычно занимает? Я могу проходить между запросами более 30 секунд, и GC, кажется, никогда не возвращает этот номер памяти в диагностической таблице обратно. Я уверен, что я ничего не знаю здесь, но просто любопытно.

РЕДАКТИРОВАТЬ 2. Теперь, когда я более подробно прочитал ссылку SO, которую Дэвид разместил выше, я начинаю думать, что это определенно проблема, которую мы наблюдаем. Если мы работаем в среде с ограниченной памятью (что мы имеем в нашей среде разработки, где мы видим это, потому что мы дешевы), у нас возникают проблемы с этим.

Редактировать 3 - Один затянувшийся вопрос. Почему память процесса постоянно увеличивается, но размер кучи не увеличивается, если это проблема LOH? На самом деле, я могу понять это сейчас. Куча — это используемая память. Выделенная процессором память — это используемая память плюс неиспользуемые фрагментированные блоки памяти.

@RemyArmstro , вы можете изменить Dictionary<int, int> на SortedDictionary<int, int> ? Словарь, вероятно, выделяет непрерывную память, может даже добавлять дополнительные данные к каждой записи. То, как реализован SortedDictionary, сделает много маленьких распределений вместо одного большого.

Изменить: если вы сериализуете в строку, а не напрямую в выходной поток ответа, это также может привести к выделению LOH.

@wanton7 В вашем ответе не хватает сути. Словарь — это только верхушка айсберга. Мы можем использовать списки, массивы и т. д. и т. д., и все они делают одно и то же. Однако, как было указано, если LOH вызывает это, как это звучит, то такое поведение, вероятно, нормально? За исключением того, что это может иметь некоторые побочные эффекты, например, что происходит, когда у вас заканчивается память? Ваше приложение просто вылетает?

@Serjster хорошо, я думал, что у вас были только небольшие случаи, когда это происходит. Для меня очень необычно иметь такие большие списки, массивы и отправлять столько данных за один вызов API, если они не двоичные. Обычно, когда у вас есть какой-то веб-API и вы получаете от него какие-то данные, вы используете пейджинг. Вы не должны отправлять 10000 записей на сторону клиента.
Но если у вас много таких проблем и нет возможности изменить работу вашего API, то я думаю, вам следует создать свои собственные реализации списков и словарей. Если вы действительно используете такие большие массивы, вы можете заменить их своими фрагментированными списками или попытаться объединить их при запуске приложения.

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

@wanton7 , ты снова упускаешь суть. Размер списка не имеет значения. Даже один элемент или небольшой список вызывают эту проблему.

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

Или от @RemyArmstro Он говорит о словарях разного размера. Я проверил corefx, и Dictionary будет выделять массив или эти

private struct Entry
{
  public int hashCode;    // Lower 31 bits of hash code, -1 if unused
  public int next;        // Index of next entry, -1 if last
  public TKey key;           // Key of entry
  public TValue value;         // Value of entry
}

Выделение 85000 байт приведет к выделению LOH, поэтому словарь с емкостью 5313 записей ключа int и значения int вызовет выделение LOH. Емкость не совпадает с числом или записями, емкость, кажется, расширяется простыми числами, проверьте частный метод изменения размера частного словаря. Каждая структура может иметь дополнительное выделение плюс заполнение памяти, поэтому даже более низкие записи могут привести к выделению LOH.

Детали реализации словаря Dictionary.cs

Изменить: фиксированный URL

@wanton7 Спасибо. Думаю, теперь мы понимаем, в чем проблема. Это просто облом, для этого нет отличного + простого решения. В основном это сводится к тому, чтобы лучше понимать это и корректировать то, как вы пишете код. Недостатком является то, что эта неуправляемая память начинает казаться немного более управляемой. :( В конце концов, у нас может быть только несколько областей, которые действительно нарушают этот лимит распределения, но одна из областей является довольно важной для нашего приложения, поэтому мы часто видим ее в настоящее время. Я думаю, что нам просто нужно переосмыслить эту часть, следите за монитором и попытайтесь найти другие области, в которых мы замечаем эту ползучесть. Еще раз спасибо!

На самом деле у нас скоро будет аналогичная ситуация, и нам нужно создать реализацию chunked IList<T> . Я собираюсь использовать битовый сдвиг фрагментов некоторого размера, поэтому я могу просто использовать битовый сдвиг и маску для индексации.

Я хотел бы знать, какой из них более полезен для GC, больший кусок или меньший кусок? От размеров от 1 КБ до 64 КБ. Меньшие куски означают больше ссылок для GC, но я предполагаю, что больший кусок может быть хуже для сжатия и фрагментации.

ваше понимание совершенно правильное - я бы выбрал не слишком большие размеры; наверное попробуй 4к/8к.

@Maoni0 спасибо!

Я выбрал 4 КБ, чтобы нас не ждали неприятные сюрпризы, если мы когда-нибудь запустим наш код под Mono. Прочитав http://www.mono-project.com/docs/advanced/garbage-collector/sgen/working-with-sgen/ , я узнал, что порог LOH составляет всего 8000 байт в Mono.

Есть ли прогресс в этом вопросе?

Мы наблюдаем ту же проблему, когда память процесса продолжает расти, несмотря на то, что размер кучи остается прежним (или уменьшается).

@sebastienros , можешь еще раз взглянуть на это? Может быть, мы можем предоставить подробные инструкции, чтобы помочь людям глубже исследовать свои сценарии?

Вот некоторые моменты, которые следует учитывать в нашей ситуации:

  • Мы возвращаем только объект Dictionary<int, int> с 1000 сохраненными целочисленными значениями. Не более того.
  • Мы конвертировали наш проект из .NET Core 2.0 --> 2.1
  • Мы наблюдаем эту проблему в MS Visual Studio 2017.

Код следующим образом:

    public async Task<IActionResult> SampleAction()
    {
        var list = new Dictionary<int, int>();
        for (int n = 0; n < 1000; n++) list.Add(n, n);
        return Ok(list);
    }

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

Я также вижу это в одном из своих проектов, но я также могу воссоздать его в совершенно новом API ядра .net, ориентированном на .Net Core 2.1 (SDK 2.1.302).

Я прикрепил пример проекта API, который я создал с помощью Visual Studio 15.8.0 Preview 4. Чтобы показать увеличение памяти, у меня было консольное приложение .net core, которое каждые полсекунды нажимало конечную точку GET значений по умолчанию, чтобы получить 2 строки. Память процесса используется медленно, но в проекте, который возвращает больше данных, она может быстро расти.

screenshot
Веб-приложение1.zip

Я нашел этот пост на бирже стека:

https://stackoverflow.com/questions/48301031/why-doesnt-garbage-collector-in-net-core-2-0-free-all-memory

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

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

image

@chrisaliotta спасибо за ссылку на этот пост, я не знал о таком поведении. Было бы действительно интересно посмотреть, объясняет ли это то, что видят люди.

@Eilon @chrisaliotta Спасибо, но это не имеет отношения к этому обсуждению. .NET не освобождает память в режиме отладки — хорошо известное поведение, поэтому мы измеряем потребление памяти (и потенциальные утечки) только в режиме выпуска. Кроме того, даже в режиме выпуска вы увидите, что память увеличивается со временем из-за режима Server GC. Так что этот пример ничего не доказывает по двум разным причинам.

@sebastienros Итак, поведение, которое мы с @beef3333 наблюдаем, соответствует ожидаемому? А именно, где Private Bytes остается повышенным, несмотря на уменьшение размера кучи? Если это так, мне кажется странным, что каждый добавочный запрос будет продолжать увеличивать приватные байты, несмотря на наличие свободного места в куче.

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

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

Тогда я протестирую ваше приложение, спасибо.

Я запускал приложение, предоставленное @beef3333, локально в течение 2 часов со скоростью 5 000 RPS, и память стабильна (в целом разница составляет 1 МБ, при 400 МБ на машине с 32 ГБ). Сборщик мусора вызывается регулярно. Я также проверил несколько дампов сверхурочно, и различные экземпляры были созданы и собраны, как и ожидалось. Я использую 2.1.1.

@sebastienros Спасибо, что изучили это. Я думаю, что вывод во всем этом заключается в следующем:

  • Управление памятью будет вести себя по-разному для веб-приложения .NET Core по сравнению с обычным настольным приложением.
  • Разработчики должны сосредоточиться на среднем потреблении памяти в виде запросов функций в секунду (RPS) за определенный период времени.
  • Рост частных байтов не всегда может свидетельствовать об утечке памяти.

Поправьте меня, если я ошибаюсь, но похоже, что .NET Core будет увеличивать выделенную память на основе средних запросов, чтобы обеспечить самое быстрое время отклика? Если это правда, можно ли предположить, что эта выделенная память, скорее всего, не будет освобождена до тех пор, пока пул приложений не будет сброшен, или она будет освобождать эту выделенную память со временем, если RPS уменьшится?

Та же проблема.
У меня есть серверная служба asp.net webapi.
Стеки: asp.net mvc, autofac, automapper, Castle.Dynamicproxy, ядро ​​Entity Framework.
Вся память будет съедена, а затем произойдет сбой службы.

Версия 2.1.0

@atpyk Обновление до 2.1.1. Если это не поможет, вы должны действительно профилировать, что хранит эту память. Я использовал https://www.jetbrains.com/dotmemory/ , но, вероятно, есть и другие инструменты, которые могут это сделать. Он может показать, что на самом деле выделено в куче больших объектов (LOH).

Вы работаете в 32-битном режиме? Поскольку выделение кучи больших объектов (больше ~85000 байт) может вызвать исключения из памяти в 32-битном режиме из-за фрагментации. Вы можете очень легко преодолеть этот предел, используя словарь. Проверьте этот комментарий https://github.com/aspnet/Home/issues/1976#issuecomment -393833505

Если вы запускаете свой код в полной версии .Net Framework, по умолчанию любой код процессора запускается в 32-битном режиме. Вам нужно снять флажок Prefer 32bit в настройках проекта или установить для некоторых параметров реестра в реестре вашего сервера значение по умолчанию 64bit.

@wanton7 Большое спасибо. Я попробую ваши решения.

Я обновился до 2.1.2 и развернул веб-приложение Microsoft Azure с помощью win-x64, но безрезультатно. @wanton7
dotnetcorememory

@atpyk, пожалуйста, создайте снимок памяти (dump_ и проанализируйте его (Visual Studio, MemoScope), чтобы увидеть, какой объект занимает всю память или просто увеличивается в счете. Вы также можете взять два и сравнить их сверхурочно.

@sabastienros Я действительно считаю, что достаточно людей выразили обеспокоенность по этому поводу, поэтому вам / MS следует начать анализировать это самостоятельно. Возможно, это работает так, как вы предполагали, но тогда дизайн ошибочен.

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

@atpyk, тогда это звучит как утечка памяти. Профилируйте свою память, чтобы увидеть, что хранит эту память, как сказал @sebastienros .

Один вопрос, вы вообще используете ASP.NET Core? Я снова прочитал ваш первый комментарий, и вы упомянули ASP.NET MVC. ASP.NET MVC и ASP.NET Core — это два совершенно разных продукта. Эти проблемы и этот репозиторий для ASP..NET Core.

Изменить: только из номеров версий звучит так, будто вы используете ASP.NET Core, но хотели убедиться.

Мы используем .net core MVC. @wanton7
Я занимаюсь анализом памяти. Возможно, Castle Dynamic Proxy приводит к утечке памяти.
memroy1
memroy2
memroy3
dynamicproxy

@Serjster Ваши программы работают в 32-битном .NET Core и аварийно завершают работу из-за нехватки памяти? Если ваш код выполняет много выделений LOH, то, вероятно, причиной является фрагментация памяти.
Если вы работаете в 32-битной среде, у вас есть два варианта: исправить свой код, чтобы избежать LOH, или переключиться на 64-битную среду.

Я не очень хорошо знаком с Azure, но после небольшого поиска в Google нашел это https://blogs.msdn.microsoft.com/webdev/2018/01/09/64-bit-asp-net-core-on-azure-app. -оказание услуг/

@Serjster Я считаю, что не все отчеты здесь одинаковы, и предпочитаю проверять достоверность каждого отдельного случая. Что-то вроде «в моем приложении есть утечка памяти» не означает, что это связано с фреймворком, поэтому я предпочитаю убедиться, что в каждом случае это действительно так.

Возьмем, к примеру, ваш случай: «Я верю», что я ответил на причину, по которой ваша память улучшается. Вы смогли исправить это после моего объяснения? Или это не решило проблему, и в этом случае вы можете дать приложение, которое я могу запустить локально, чтобы воспроизвести проблему?

@sebastienros В итоге мы обнаружили, что выделение памяти продолжало увеличиваться, хотя потребление памяти не увеличивалось. Я знаю, что ядро ​​​​ASP.NET выполняет некоторые эвристические действия, чтобы сообщить ему, что оно должно захватывать больше, но, похоже, оно постоянно выделяло все больше и больше на каждый запрос. В этом отношении мне это кажется почти жадным, но я могу ошибаться.

В любом случае, я думаю, что смысл @Serjster в том, что эта тема продолжает расти, потому что здесь явно есть некоторая путаница. На старой земле ASP.NET (я полагаю, до ядра 1) нам не нужно было видеть такое поведение (по крайней мере, не в таком масштабе. На самом деле это может быть не ошибка/проблема, но это определенно то, что вызывает у многих людей снова и снова задавать один и тот же вопрос.

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

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

Я поддерживаю это. Нам было бы полезно узнать, почему .NET Core 2.1 более «гибок», когда дело доходит до управления памятью, чем предыдущие версии.

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

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

Параллельно я буду работать над документом, в котором перечислены все рекомендации, известные проблемы с памятью (LOB, HttpClient, ...) и способы анализа и сообщения об этих проблемах.

Просто чтобы заверить вас, что мы заботимся об этой проблеме, и для упреждающего обнаружения утечек памяти, в течение последних 6 месяцев мы непрерывно запускали приложения ASP.NET в Azure, как в Linux, так и в Windows, в течение 24 часов и 7 дней. Эти тесты извлекают последний исходный код ASP.NET на каждой итерации (ежедневно или еженедельно). Мы измеряем RPS, задержку, использование ЦП и памяти. Приложение использует EF Core, MVC и Razor и поддерживается на уровне 50 % ЦП для имитации значительной нагрузки.

Вы можете увидеть результаты публично здесь , на этом сайте (просмотр , чтобы найти ежедневный отчет): https://msit.powerbi.com/view?r=eyJrIjoiYTZjMTk3YjEtMzQ3Yi00NTI5LTg5ZDItNmUyMGRlOTkwMGRlIiwidCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsImMiOjV9&pageName=ReportSectioneeff188c61c9c6e3a798

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

@sebastienros Спасибо за обновления. Я знаю, что в нашем случае проблема была больше связана с новой проблемой «жадного распределения памяти», которую мы изначально приняли за утечку памяти. Я даже не уверен, что сейчас есть проблема, возможно, новая эвристика оптимизации намного более агрессивна. Не уверен... но я думаю, что вы на правильном пути, действительно оценивая эту ветку и придумывая некоторые сводные объяснения/резюме того, что люди видят/недопонимают. Удачи!

Все отдельные отчеты были изолированы и получат отдельные последующие действия и будут закрыты, если они уже решены. Не стесняйтесь подписываться на них, чтобы быть в курсе.

Параллельно я буду работать над документом, в котором перечислены все рекомендации, известные проблемы с памятью (LOB, HttpClient, ...) и способы анализа и сообщения об этих проблемах.

Это огромный +1 от меня. Я чувствую, что одна из самых больших проблем здесь заключается в том, насколько сложно «чувствовать» сбор информации, чтобы затем попытаться помочь определить, в чем состоит проблема. Наличие некоторых отличных документов, которые могут позволить нам следовать некоторым инструкциям, чтобы (i) собрать, (ii) попытаться провести самодиагностику и (iii) опубликовать наши дампы / результаты таким образом, чтобы это было эффективно для команды MS, действительно может помочь обоим стороны забора.

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

Еще раз спасибо, что выслушали @sebastienros - очень ценю, приятель!

Что вы думаете о ситуации на картинке ниже?

Мы запускаем 4 веб-приложения в рамках одного плана. Первоначально это был B1, масштабированный до S2, и память продолжала расти, пока я не установил голодное веб-приложение csproj:

<ServerGarbageCollection>false</ServerGarbageCollection>

а также отключить Application Insights

  1. Я полагаю, что, поскольку память можно держать под контролем с помощью вышеуказанных настроек, памяти не будет. Правильный?
  2. Является ли представленное поведение нормальным?

memory eaten up

Здесь та же ситуация, что и у @alexiordan : у нас была консоль .net core 2.1, на которой запускались некоторые IHostedServices, размещенные в Kube, ИЗ Microsoft/ dotnet:2.1-runtime AS base. Мы хотели включить HealthChecks, поэтому добавили asp.net только с промежуточным ПО HealthChecks и изменили базовый образ на microsoft/ dotnet:2.1-aspnetcore-runtime. Результатом стал ООМ. Нам удалось стабилизировать выделение памяти, добавивложный в кспродж.

Наш анализ показал, что в приложении asp.net GC собирает реже, а Finalizer Queue реже обходит.

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

Система.GC.Собрать();
System.GC.WaitForPendingFinalizers();
Система.GC.Собрать();

распределение памяти оставалось стабильным.

Что вы думаете о ситуации на картинке ниже?

Мы запускаем 4 веб-приложения в рамках одного плана. Первоначально это был B1, масштабированный до S2, и память продолжала расти, пока я не установил голодное веб-приложение csproj:

<ServerGarbageCollection>false</ServerGarbageCollection>

а также отключить Application Insights

  1. Я полагаю, что, поскольку память можно держать под контролем с помощью вышеуказанных настроек, памяти не будет. Правильный?
  2. Является ли представленное поведение нормальным?

memory eaten up

Привет @alexiordan

Мы также видим очень похожий профиль памяти при использовании ИИ (веб-приложение на net core 2.1). Продвинулись ли вы дальше в решении этой проблемы? Очевидно, мы хотим оставить ИИ в приложениях.

Странно, что использование растет с каждым запросом, но установка выше значения false, кажется, останавливает это для меня? странно, потому что вы думаете, что true будет значением, которое его включает, но кажется, что все наоборот...

Забыл упомянуть, что вскоре после объявления о намерении написать статью о проблемах, описанных в этой ветке, я действительно это сделал. Вы можете увидеть это здесь: https://github.com/sebastienros/memoryleak

Он поставляется с небольшим приложением, которое отображает паттерны на графике в реальном времени.

но установка приведенного выше значения false, кажется, останавливает это для меня? странно, потому что вы думаете, что true будет значением, которое его включает, но кажется, что все наоборот...

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

Установив для SGC значение false, мой основной API asp.net упал со 150 МБ до 48 МБ и после этого не увеличивался при каждом запросе. Так на самом деле, это лучшая настройка для производства на данный момент?

@kgrosvenor на самом деле, это зависит. Цитата из отличной статьи @sebastienros :

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

Спасибо, это очень удобно, я буду иметь в виду - буду следить за этой веткой для получения дополнительных обновлений, при этом я очень люблю ядро ​​​​asp.net :)

Также страдаете от этого с консольным приложением .net core 2.1. постоянный рост памяти. установили контейнеры докеров на низкий максимум, чтобы он срабатывал и перезапускался, что работает нормально, но это уродливо.

Есть новости с этой стороны? У нас также есть такое же поведение в ASP.Net Core v2.1. Из того, что я вижу в коммите https://github.com/aspnet/AspNetCore/commit/659fa967a1900653f7a82f02624c7c7995a3b786 , похоже, возникла проблема с управлением памятью, которая будет исправлена ​​в версии 3.0?

@flo8 Вы пробовали обновиться до 2.2 и проверили поведение? https://dotnet.microsoft.com/download

Запуск последней версии 2.2, а также наличие этой проблемы - простое создание списка из 1 миллиона целых чисел и возврат emptyResult() увеличит мою кучу на несколько сотен МБ при каждом запросе, пока у меня не закончится память. Установка для ServerGarbageCollection значения false вылечила его, хотя это не похоже на правильное исправление...

@ dre99gsx , кажется, у вас есть простая копия, не могли бы вы поделиться проектом и шагами, чтобы я мог сделать то же самое локально?

В этом случае он должен заполнить LOB, но они должны быть собраны на gen2. Также, пожалуйста, поделитесь, в какой среде я могу воспроизвести это, ОС, память, загрузка.

Извините за загадочный ответ. Довольно легко:
(Windows 7 64bit, 16GB ram, Google Chrome для http-запросов, сообщество VS2017) - я все еще привыкаю добавлять код в эти потоки, простите за внешний вид)

  • запустить новое веб-приложение .NET Core 2.2
  • Внедрите класс обслуживания с ограниченной областью действия (ничего особенного) в конструктор контроллера.
  • Попросите действие Index() контроллера вызвать метод этого класса обслуживания.
  • Создайте класс модели (DumbClass) с одним свойством: public int ID {get; задавать;}
  • В методе класса обслуживания создайте экземпляр списка и заполните его:
    var lst = новый список();
    for (i=0; i<10000000; i++) <--- примечание: 10 миллионов итераций
    {
    lst.add(новый DumbClass(){ID=i});
    }
  • return из метода, не нужно ничего возвращать, но вы также можете передать этот список обратно...
  • index() возвращает новый EmptyResult();

Теперь просто вызывайте действие каждые 8 ​​секунд и наблюдайте, как увеличивается объем памяти профилировщика. В моей системе это то, что я вижу в Private Bytes:

Стартовое приложение: 155 МБ
1-й HTTP-запрос на получение: 809 МБ
2-й: 1,2 ГБ
3-й: 1,4 ГБ
4-й: 1,8 ГБ
5-й: 2,1 ГБ... 2,3 ГБ... 2,6 ГБ...

Теперь, в какой-то момент, GC, кажется, срабатывает, но в этом примере он никогда не опускается ниже 3 ГБ. Как уже упоминалось, если я устанавливаю для ServerGC значение false, он никогда не поднимается выше 1 ГБ, хотя он все равно поднимается туда и колеблется на уровне 1 ГБ.

Дайте мне знать, если это поможет, и если вы можете воспроизвести его. О, я прочитал ваш пост на github: «Управление памятью и шаблоны в ASP.NET Core», фантастическая статья, спасибо за участие!

@dre99gsx 👋

Я все еще привыкаю добавлять код в эти темы, простите за внешний вид)

Вообще никаких проблем :) Вопрос: не могли бы вы разместить весь образец приложения на GitHub (бесплатный репозиторий) или где-то в этом роде? Это самый простой способ для всех остальных быстро клонировать/загрузить _целый_ образец приложения/репозитория, с которым вы играли.

Кроме того, снимки экрана с использованием памяти с использованием TaskManager помогут (если в Windows - или эквивалент на *nix .. который является командой top ??)

Пока большие усилия!

/me возвращается к тому, чтобы молча и серьезно наблюдать за этой веткой.

Напоминаем, что вы можете увидеть некоторые демонстрации и объяснение всех симптомов, описанных в теме, здесь: https://github.com/sebastienros/memoryleak

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

@dre99gsx dre99gsx Теперь, возвращаясь к недавнему комментарию, я призываю вас создать отдельную проблему из этой темы. Сидя в телефоне, я не понял, что это был «этот» ;). Из вашего первого комментария вы заявляете

пока не закончится память

Поэтому я ожидаю исключения OutOfMemory, поэтому я попросил репродукцию. Но в своем следующем комментарии вы заявляете:

Теперь, в какой-то момент, кажется, что GC срабатывает, но в этом примере он никогда не опускается ниже 3 ГБ.

Так что проблем с памятью нет. Это типично для машины с большим количеством ядер и большим объемом доступной памяти. Сборщик мусора освободит управляемые кучи, но память все равно будет выделена, так как нет причин для ее отмены (много доступной памяти). Это стандартное поведение в .NET, и я демонстрирую его в статье, на которую указал. Вы также можете прочитать это: https://blogs.msdn.microsoft.com/maoni/2018/11/16/running-with-server-gc-in-a-small-container-scenario-part-0/

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

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

Правильно, когда я сказал «недостаточно памяти», я основываюсь на том, что визуально не вижу памяти, доступной для любого другого приложения через частный рабочий набор Windows (диспетчер задач); не "Исключение нехватки памяти".

Знаешь что, ты прав. Это все больше и больше похоже на ожидаемое поведение, и если доступно так много памяти, зачем тратить ресурсы на ее освобождение, если она никому не нужна! Я понимаю. Пока GC достаточно умен, чтобы освободить память, чтобы другие приложения не были ограничены , я просто оставлю его как есть и позволю ему делать свое дело. Спасибо еще раз!

Я разработал свое приложение в ядре Asp.net 2.2, также столкнувшись с той же проблемой, связанной с освобождением памяти.
каждый вызов увеличивает память в пределах 40-50 мб, каждый раз никогда не освобождается.

Я также добавил упомянутый тег ServerGarbageCollection>false
по-прежнему сталкивается с той же проблемой для 50 пользователей, он использует около 2 ГБ ОЗУ в режиме In-Process (рабочий процесс w3wp iis)

Пожалуйста помоги.

Пожалуйста помоги !! та же проблема, что и у ankitmori14

@ankitmori14 , @ikourfaln — если вы прочитали комментарий @sebastienros по адресу https://github.com/aspnet/AspNetCore/issues/1976#issuecomment-449675298 и все еще считаете, что проблема с памятью, отправьте новую заявку. с подробными инструкциями по воспроизведению проблемы, а также любой другой информацией и трассировками, которые у вас есть о поведении. Пожалуйста, помните, что если в приложении/процессе действительно нет ошибок, маловероятно (но не невозможно) наличие ошибки. Сборщик мусора «Сервер» по умолчанию не пытается использовать минимально возможный объем памяти; он скорее срабатывает, когда это необходимо , например, когда память на самом деле заканчивается. Таким образом, даже маленькое приложение может использовать 2 ГБ памяти, и это не проблема, потому что еще 4 ГБ свободно.

Привет,

У нас возникла эта проблема: https://stackoverflow.com/questions/53868561/dotnet-core-2-1-hoarding-memory-in-linux

В основном память растет в течение нескольких дней в процессе, пока Kubernetes не убьет ее, потому что не достигнет настроенного предела в 512 МБ. Забавно, но память резко сократилась при создании дампа памяти, без перезапуска процесса или чего-то еще. Изучив дамп памяти, мы увидели множество объектов без рута.

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

<PropertyGroup>
  <ServerGarbageCollection>false</ServerGarbageCollection>
  <ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>
</PropertyGroup>

Вопрос @vtortola , когда вы настроили ограничение в 512 МБ для своего приложения, имели ли вы представление о количестве одновременных запросов, которые вы хотели обработать, или проверили, сколько одновременных запросов может обработать ваше приложение, прежде чем упасть ??

Мы провели несколько предварительных и грубых тестов и проверили, можем ли мы обрабатывать 500 одновременных веб-сокетов на модуль, используя 512 МБ. Мы часами работали с 2 модулями и 1000 одновременных подключений с объемом памяти менее 150 МБ. Развернутое приложение с 2 модулями имеет от 150 до 300 одновременных подключений в любой момент, а объем памяти варьируется от менее 100 МБ в первые несколько дней до достижения 512 МБ примерно через 2 недели. Кажется, нет корреляции между количеством подключений и используемой памятью. Более 70% соединений длятся 10 минут.

Не могли бы вы поделиться дампом памяти, когда он составляет 100 МБ и 512 МБ, чтобы увидеть, какие экземпляры все еще живы?

Боюсь, я не могу поделиться дампом, так как он содержит куки, токены и много приватных данных.

Можете сравнить то локально то? С точки зрения того, какой объект занимает больше всего памяти, и если их количество не коррелирует с вашей нагрузкой. Например, если у вас 300 подключений, не должно быть объектов подключения 3K.

К сожалению, проверка установки <ConcurrentGarbageCollection>false</ConcurrentGarbageCollection> не помогла, и процесс все еще копит память.

У нас есть линуксовый дамп процесса, единственный инструмент, который у нас есть для его анализа, — это lldb, а мы в этом деле совсем нубы.

Вот некоторые данные на случай, если это прозвенит:

(lldb) eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x00007F8481C8D0B0
generation 1 starts at 0x00007F8481C7E820
generation 2 starts at 0x00007F852A1D7000
ephemeral segment allocation context: none
         segment             begin         allocated              size
00007F852A1D6000  00007F852A1D7000  00007F853A1D5E90  0xfffee90(268430992)
00007F84807D0000  00007F84807D1000  00007F8482278000  0x1aa7000(27947008)
Large object heap starts at 0x00007F853A1D7000
         segment             begin         allocated              size
00007F853A1D6000  00007F853A1D7000  00007F853A7C60F8  0x5ef0f8(6222072)
Total Size:              Size: 0x12094f88 (302600072) bytes.
------------------------------
GC Heap Size:            Size: 0x12094f88 (302600072) bytes.

И результат dumpheap -stat : https://pastebin.com/ERN7LZ0n

Вы можете найти, как Grafana сообщала о состоянии памяти в https://stackoverflow.com/questions/53868561/dotnet-core-2-1-hoarding-memory-in-linux .

У нас есть еще одна группа сервисов в ядре dotnet, которые работают безупречно, хотя и не используют веб-сокеты.

@vtortola , не могли бы вы создать новую проблему, чтобы мы не разветвляли эту. Я думаю, что тот факт, что он использует WebSockets, делает его уникальным, и нам нужно создать хотя бы образец приложения, которое будет вести себя как ваше и подвергать его нагрузке в течение длительного периода времени. Не могли бы вы описать, что ваши клиенты делают с веб-сокетом, как они подключаются к нему и как долго? Я мог бы настроить подобное приложение и запускать его в течение нескольких дней, а также делать дампы памяти, если вижу, что объем памяти увеличивается.

Это также может быть конфликт между сборщиком мусора и Kubernetes, из-за которого сборщик мусора не получает предупреждения о том, что он близок к пределу пода.

@richlander работает над похожей проблемой. Рич, может ли это дело (прочитайте только последние 6 комментариев) быть связано с тем, над чем вы работаете?

если это имеет значение, у меня такая же (или очень похожая) проблема. У меня есть более дюжины консольных приложений .net core 2.1 и 2.2, работающих на докере - каждый контейнер с ограничением памяти 512 МБ, также с использованием веб-сокетов (клиент), и приложения достигают предела памяти контейнера каждые ~ 3 дня. Затем контейнер выключается и перезапускается.

Все использует аутентифицированные сервисы, поэтому я не могу предоставить то, что у меня есть, но я мог бы собрать что-то вместе, используя тестовый сайт, чтобы воспроизвести проблему.

Какие параметры вы используете для установки пределов памяти? Просто --memory или также --memory-swap ?

Когда я тестирую его, я вижу, что он учитывается очень хорошо, никогда не превышая установленный мной предел, даже с очень низким пределом, таким как 16 МБ, однако он меняет местами на диске как сумасшедший. И я тестирую приложение, которое выполняет запросы к базе данных (EF) и рендеринг представлений Razor.

@sebastienros абсолютно, я создал https://github.com/aspnet/AspNetCore/issues/6803

Дайте мне знать, если вам нужна дополнительная информация.

В настоящее время у меня проблемы с памятью.

Через час работы вся память используется процессом w3wp.exe (под управлением .NET Core InProcess). Смена GC на рабочую станцию ​​мне не помогла.

Проанализировав дамп памяти, я обнаружил похожую проблему, https://github.com/aspnet/AspNetCore/issues/6102.

Я надеюсь протестировать его в производственной среде позже сегодня, после обновления до последней версии среды выполнения .NET Core, в настоящее время 2.2.3. Я дам вам знать, как это происходит.

У меня похожие проблемы с использованием памяти. Если я устанавливаю ограничения на свои контейнеры Linux, это просто ускоряет OOM. Это происходит даже при использовании базовых шаблонов в Visual Studio. Я нашел одну вещь - затронуты Core 2.1 и 2.2. Core 2.0 нет - но это EOL :(

См. выше

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

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

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

@PrefabPanda убедитесь, что вы действительно работаете в режиме GC рабочей станции, проверив, что System.Runtime.GCSettings.IsServerGC равно false .

Тогда это может быть настоящая утечка памяти. Открыть отдельную тему, может быть, лучший шаг.

@ wanton7 - спасибо, я перепроверил, и это определенно установлено.

@DAllanCarr - подойдет

Я почти уверен, что это не утечка памяти. Больше похоже на то, что настройки Docker могут быть применены неправильно, и сборщик мусора не сработает до достижения предела. Я знаю, что 3.0 вносит исправления в этом смысле.

@richlander эта проблема похожа на то, что, как известно, не работает в 2.2?

@PrefabPanda , не могли бы вы поделиться точной версией докера, которую вы используете, и файлом компоновки докера? Я пытаюсь воспроизвести, но между версиями docker и docker-compose мне трудно воспроизвести эту проблему локально.

@sebastienros , @richlander - Спасибо, что ответили мне. Я очень ценю это.

Мои версии докера:

Сообщество Docker для настольных ПК
Версия 2.0.0.2 (30215)

Двигатель: 18.09.1
Составить: 1.23.2

См. во вложении весь тестовый проект:
Веб-приложение1.zip

тестовые запросы curl.zip

На всякий случай мой Dockerfile:

С mcr.microsoft.com/dotnet/core/aspnet:2.2 База AS
РАБОЧИЙКАТАЛОГ /приложение
ЭКСПОЗИЦИЯ 80
ВЫСТАВИТЬ 443

С mcr.microsoft.com/dotnet/core/sdk:2.2 AS сборка
РАБОЧИЙ КАТАЛОГ /источник
КОПИРОВАТЬ ["Веб-приложение1/Веб-приложение1.csproj", "Веб-приложение1/"]
RUN восстановление dotnet "WebApplication1/WebApplication1.csproj"
КОПИРОВАТЬ . .
РАБОЧИЙ КАТАЛОГ "/src/WebApplication1"
ЗАПУСТИТЬ сборку dotnet "WebApplication1.csproj" -c Release -o /app

ИЗ сборки КАК опубликовать
ЗАПУСТИТЬ публикацию dotnet "WebApplication1.csproj" -c Release -o /app

ОТ базы КАК окончательная
РАБОЧИЙКАТАЛОГ /приложение
КОПИРОВАТЬ --from=publish /app .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]

докер-compose.yml:

версия: "2.4"

Сервисы:
веб-приложение1:
изображение: ${DOCKER_REGISTRY-}веб-приложение1
mem_reservation: 128м
мем_лимит: 256 м
memswap_limit: 256 м
процессор: 1
строить:
контекст: .
файл докеры: WebApplication1/Dockerfile

docker-compose-override.yml:

версия: "2.4"

Сервисы:
веб-приложение1:
окружающая обстановка:
- ASPNETCORE_ENVIRONMENT=Разработка
- ASPNETCORE_URLS=https://+:443;http://+:80
- ASPNETCORE_HTTPS_PORT=44329
- DOTNET_RUNNING_IN_CONTAINER=истина
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=истина
- ASPNETCORE_preventHostingStartup=true
порты:
- "50996:80"
- "44329:443"
тома:
- ${APPDATA}/ASP.NET/Https:/root/.aspnet/ https:ro
- ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/us ersecrets:ro

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

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

@PrefabPanda , когда вы сказали: «Я пытался вызвать это явно», не могли бы вы уточнить, что именно означает? GC видит правильное ограничение памяти контейнера, если оно указано, независимо от того, срабатывает ли оно естественным образом или индуцированно. если вы вызвали GC.Collect(), он выполнит полную блокировку сбора; и если вы выполните GC.Collect(2, GCCollectionMode.Default, true, true), он выполнит полное сжатие GC, когда это вернет, куча имеет наименьший возможный размер, независимо от ограничения контейнера или чего-либо еще.

Привет @Maoni0 - я пробовал GC.Collect(2, GCCollectionMode.Default, правда, правда)

Я только что видел еще один комментарий, в котором говорится, что 256 МБ слишком мало для 2.2, но может быть «исправлено» в 3.0. Похоже придется еще поэкспериментировать...

если вы попробовали GC.Collect(2, GCCollectionMode.Default, true, true) и память не уменьшилась, как вы ожидали, это означает, что у вас настоящая утечка памяти. не уверен, сколько инструментов доступно в вашей среде. можно ли вообще запускать команды sos? если это так, вы всегда можете посмотреть, что осталось в куче после вашего индуцированного GC

Спасибо @Maoni0 - имейте в виду, что я использую шаблон в Visual Studio - в этом тестовом проекте, который я приложил, буквально нет моего собственного кода. У меня есть еще несколько отзывов, которые нужно проработать, я свяжусь с вами. Большое спасибо.

@ Maoni0 - я пытался установить более высокий предел, без разницы. Похоже, мне придется попробовать предварительную версию 3.0

Я хотел бы заблокировать комментарии по этому вопросу, поскольку он очень старый, и все его случаи рассматриваются в отдельных выпусках. Я прокомментировал это по ошибке, думая, что комментирую https://github.com/aspnet/AspNetCore/issues/10200 .

Спасибо

Была ли эта страница полезной?
0 / 5 - 0 рейтинги