У меня есть служба C#, которая слушает очередь для XML-сообщений, получает их, обрабатывает их с помощью XSLT и записывает их в базу данных. Он обрабатывает около 60 тыс. Сообщений в день около 1 Мб каждый. Память при простоя идет до 100 МБ, что действительно хорошо. Однако недавно я начал обрабатывать сообщения размером 12 МБ. Он продувает память и даже когда она не работает, она имеет память около 500 МБ. Любые предложения, почему это может быть проблемой? Я не думаю, что есть утечка памяти, поскольку она всплыла бы после обработки так много (60 КБ сообщений 1 МБ).Проблемы с памятью в .NET
ответ
Это выглядит отлично. Почему вы думаете, что это проблема?
Сборщик мусора в конечном итоге освободит неиспользуемую память, но это не означает, что это произойдет, как только ваша служба будет работать.
Raymond Chen написал хорошую статью, описывающую основную идею сбора мусора:
Однако, - но это чистая спекуляция с информацией, представленной в ваших вопросах - там могут быть утечки памяти, связанные с методами расширения в вашем XSLT. Методы расширения могут привести к проблемам при перекомпиляции таблицы стилей каждый раз при преобразовании нового документа XML. Исправление прост: после компиляции кешируйте таблицу стилей.
Очень сложно сказать, что может быть проблемой. У меня был успех с использованием Ants Memory Profiler при исследовании проблем с памятью.
Вопреки мысли Я не думаю, что есть утечка памяти; Я бы profile память.
Какую меру вы используете для памяти? Существует множество возможных мер, и ни один из них не означает «используемая память». С виртуальной памятью, совместным использованием изображений и т. Д. Все не так просто.
даже тогда, когда находится в режиме ожидания он имеет память о 500MB
Большинство процессов (и я думаю, что это включает в себя .NET) не освобождает память обратно в ОС сразу, выделенной процессу. Но если он не используется, он будет выгружаться из физической памяти, позволяя другим процессам использовать его.
начал обработку сообщений от 12 МБ в размере
Если XML-документ раскладывается в объектной модели (например, XmlDocument
) она требует много больше памяти (много ссылок на другие узлы). Либо посмотрите на другую модель (как XDocument
, так и XPathDocument
- более легкий вес) или, лучше, обработать XML с помощью XmlReader
и, следовательно, никогда не иметь полностью расширенную объектную модель.
SIR! ПОЗВОНИТЕ ЗАДАЧУ МЕНЕДЖЕРА И ПРОГУЛКА. Шутки в сторону. Управление памятью в .NET не настроено на минимальный размер. Он настроен на эффективность. Он будет храниться в памяти, а не отпускать его обратно в систему. Не поддавайтесь соблазну прислушиваться к своей памяти, если нет реальной проблемы (исключения OOM, проблемы с системой).
Прежде всего, добавьте что-то в свою систему, чтобы вы могли вручную вызвать сбор мусора для целей расследования. CLR не обязательно возвратит память к O/S, если она не находится в дефиците. Вы должны использовать этот механизм для ручного вызова сборки мусора, когда у вас есть система в состоянии покоя, чтобы вы могли увидеть, падает ли память. (Вы должны дважды вызвать GC.Collect(2)
, чтобы объекты с финализаторами были фактически собраны, а не только финализированы.)
Если память падает до уровня покоя после полного GC, то у вас нет проблемы: просто это .NET не проявляя активности при освобождении памяти.
Если память не опускается, у вас есть какая-то утечка. Поскольку ваши сообщения большие, это означает, что их текстовые представления, скорее всего, заканчиваются на кучи больших объектов (LOH). Эта куча не уплотняется, что означает, что легче утечка этой памяти, чем с другими кучами.
Одна вещь, которой нужно следить, это интернирование внутри строки: если вы вручную выполняете строки, это может привести к фрагментации LOH.
делает компиляторы интернированием строковых литералов, вызывая такую же фрагментацию? –
@Matt Briggs: Нет, компилятор фактически не выполняет интернирование как таковое, вместо этого все строковые литералы скомпилируются таким образом, что при запуске программы будет создан только один строковый объект. –
Я проверил бы обработчики событий. Если вы не будете осторожно отделять их, когда закончите, кажется, что создавать ссылки на объекты, которые не будут собираться GC, легко.
Я не знаю, что это самая большая практика (так как бремя начала и отмены обработки событий должно упасть до подписчика), но я пошел по пути реализации IDisposable и создания своих событий с явным экземпляром поля делегата. При удалении поле может быть установлено в нуль, что эффективно отключает все подписки.
что-то вроде этого:
public class Publisher
: IDisposable
{
private EventHandler _somethingHappened;
public event EventHandler SomethingHappened
{
add { _somethingHappened += value; }
remove { _somethingHappened -= value; }
}
protected void OnSomethingHappened(object sender, EventArgs e)
{
if (_somethingHappened != null)
_somethingHappened(sender, e);
}
public void Dispose()
{
_somethingHappened = null;
}
}
Запрещая что (я не знаю, сколько у вас контроля над издателем), вам, возможно, нужно быть осторожным, чтобы отделить ненужные обработчики вашего абонента:
public class Subscriber
: IDisposable
{
private Publisher _publisher;
public Publisher Publisher
{
get { return _publisher; }
set {
// Detach from the old reference
DetachEvents(_publisher);
_publisher = value;
// Attach to the new
AttachEvents(_publisher);
}
}
private void DetachEvents(Publisher publisher)
{
if (publisher != null)
{
publisher.SomethingHappened -= new EventHandler(publisher_SomethingHappened);
}
}
private void AttachEvents(Publisher publisher)
{
if (publisher != null)
{
publisher.SomethingHappened += new EventHandler(publisher_SomethingHappened);
}
}
void publisher_SomethingHappened(object sender, EventArgs e)
{
// DO STUFF
}
public void Dispose()
{
// Detach from reference
DetachEvents(Publisher);
}
}
Я ожидаю, что память полностью исчезнет, когда служба простаивает, и собрана сборка мусора. Также, если многие из этих огромных сообщений приходят в службу, это исключает исключение из памяти. – koumides
@koumides: Ваша память не будет высвобождена, как только ваша служба простаивает, сборщик мусора оптимизирован для максимального освобождения памяти в зависимости от назначений. – James
Как вы измеряете использование памяти? Диспетчер задач - это не лучший способ ... профилировщик, такой как CLR Profiler, даст вам лучшее представление о том, сколько памяти выделяется и используется. –