Использование памяти на сайте ASP.NET довольно высокое - PullRequest
18 голосов
/ 04 августа 2010

У меня есть веб-сайт ASP.NET, который использует около 2 ГБ физической памяти в течение 3-4 дней, что для меня звучит очень плохо.На данный момент я настроил IIS для перезапуска процесса пула приложений, когда он достигает 500 МБ.Я хотел бы попытаться отследить проблему.

При создании нового экземпляра объекта в .NET у меня сложилось впечатление, что его не нужно освобождать, поскольку сборщик мусора .NET сделает это за меня.

Это тот случай или это может быть одной из причин, по которой у меня возникли проблемы?

Ответы [ 7 ]

17 голосов
/ 04 августа 2010

.NET очень эффективно справится со сборкой мусора. Хотя для типов, реализующих IDisposable, целесообразно вызывать метод Dispose, это, вероятно, не ваша проблема. Утечки памяти могут происходить в .NET по многим причинам. Вот некоторые из них:

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

Надеюсь, это даст вам представление о том, где искать.

ОБНОВЛЕНИЕ: Вы должны посмотреть это видео об отладке ASP.NET.

ОБНОВЛЕНИЕ 2: По поводу вашего комментария мой ответ следующий. CLR соберет всю управляемую память, поэтому будут собраны все объекты, которые вы создаете с помощью new. В этом смысле не имеет значения, реализует ли объект IDisposable или нет. Однако во многих случаях вам необходимо прямо или косвенно использовать собственные ресурсы (такие как дескрипторы файлов, графические дескрипторы, соединения с базой данных, использование собственной неуправляемой памяти). CLR не знает, как освободить эти ресурсы. Для этого в .NET есть понятие финализаторов. Финализатор - это виртуальный метод, который может реализовать разработчик класса. Когда вы сделаете это, CLR вызовет этот метод после того, как экземпляр этого типа станет не связанным, и до того, как он будет собран. Финализаторы обычно содержат логику, которая освобождает эти ресурсы. Другими словами, когда типу нужны собственные ресурсы, он обычно имеет метод финализатора, который позволяет типу освобождать эти ресурсы.

Что касается CLR, история заканчивается здесь. В CLR нет специальной обработки объектов, которые реализуют интерфейс IDisposable. Однако сборщик мусора в .NET недетерминирован по своей природе. Это означает, что вы не знаете, когда он работает и работает ли он. Это означает, что может пройти очень много времени, прежде чем ваши родные ресурсы будут очищены (потому что финализатор будет вызываться только после сбора). Однако для многих ресурсов необходимо освободить их, как только вы закончите с ними. Например, вы стремитесь быстро исчерпать соединения с базой данных, когда не закрываете их или когда вы работаете с GDI + в .NET через пространство имен System.Drawing).

По этой причине был введен интерфейс IDisposable. Опять же, CLR и сборщик мусора не смотрят на этот интерфейс. Это контракт между типом и его пользователями, позволяющий пользователям напрямую высвобождать базовые ресурсы объекта. В нормальном дизайне и финализатор объекта, и метод Dispose объекта будут вызывать один и тот же закрытый или защищенный метод, который освободит эти ресурсы. Когда тип реализует IDisposable, разумно будет вызывать его метод Dispose, когда вы покончили с ним, или обернуть объект в оператор using, чтобы разрешить освобождение собственных ресурсов.

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

Надеюсь, это имеет смысл.

6 голосов
/ 04 августа 2010

Может быть много причин для вашего высокого использования памяти, но сборка мусора в .NET - очень точная вещь. То есть это много для вас, но иногда не так, как вы ожидаете.

В частности, он может очищать только те объекты, на которые нет активных ссылок, поэтому, если вы закончили с классом, но что-то еще имеет ссылку на него, вам нужно удалить эту ссылку, чтобы GC восстановить эту память для вас. Кроме того, если у вас есть какие-либо неактивные открытые соединения (скажем, с БД или чем-то еще), не забудьте закрыть и утилизировать их. Обычно мы обертываем такие объекты в using блоки, например:

using(SqlConnection conn = new SqlConnection(myConnString))
{ ...rest of code here }

Это автоматически закроет и избавится от соединения. Это реализовано как блок try ... finally, поэтому соединение будет закрыто, даже если в блоке using возникло исключение.

Кроме этого, ответом будет «профиль, профиль, профиль», пока вы не найдете свою утечку / узкое место / что угодно.

3 голосов
/ 04 августа 2010

Есть несколько вещей, на которые вы должны обратить внимание:

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

Во-вторых, избавление от предметов. Сборщик мусора .NET избавит вас от ссылок, но когда вы создаете объекты, реализующие интерфейс IDisposable, вы всегда должны использовать ключевое слово using.

using(Foo foo = new Foo())
{
    ...
}

эквивалентно действию:

Foo foo;
try
{
    foo = new Foo();
    ...
}
finally
{
    foo.Dispose();
}

И это обеспечит эффективную утилизацию ваших объектов.

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

1 голос
/ 04 августа 2010

Используйте профилировщик памяти (либо в дампе памяти, либо в вашей рабочей среде), когда ваша память превышает 500 МБ, чтобы понять, какие объекты занимают все это пространство.

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

1 голос
/ 04 августа 2010

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

Правило большого пальца:

Если вы используете += для подписки на событие, то вам нужно использовать -= в методе Dispose() для отмены подписки.

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

1 голос
/ 04 августа 2010

Утечки памяти в .NET все еще возможны. Это правда, что по большей части вам не нужно освобождать объекты (есть несколько исключений, таких как объект Graphics), но если вы сохраните ссылки на них, они не будут собирать мусор, поскольку на них все еще ссылаются. 1001 *

Если GC видит, что на объект ссылаются где-либо, в любом месте вашего приложения, он не будет его уничтожать.

Ознакомьтесь с этой Code Project статьей об утечках памяти .NET и о том, как их найти и исправить.

0 голосов
/ 04 августа 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...