2010-09-24 2 views
3

У меня есть служба C#, которая слушает очередь для XML-сообщений, получает их, обрабатывает их с помощью XSLT и записывает их в базу данных. Он обрабатывает около 60 тыс. Сообщений в день около 1 Мб каждый. Память при простоя идет до 100 МБ, что действительно хорошо. Однако недавно я начал обрабатывать сообщения размером 12 МБ. Он продувает память и даже когда она не работает, она имеет память около 500 МБ. Любые предложения, почему это может быть проблемой? Я не думаю, что есть утечка памяти, поскольку она всплыла бы после обработки так много (60 КБ сообщений 1 МБ).Проблемы с памятью в .NET

+1

Я ожидаю, что память полностью исчезнет, ​​когда служба простаивает, и собрана сборка мусора. Также, если многие из этих огромных сообщений приходят в службу, это исключает исключение из памяти. – koumides

+1

@koumides: Ваша память не будет высвобождена, как только ваша служба простаивает, сборщик мусора оптимизирован для максимального освобождения памяти в зависимости от назначений. – James

+1

Как вы измеряете использование памяти? Диспетчер задач - это не лучший способ ... профилировщик, такой как CLR Profiler, даст вам лучшее представление о том, сколько памяти выделяется и используется. –

ответ

7

Это выглядит отлично. Почему вы думаете, что это проблема?

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

Raymond Chen написал хорошую статью, описывающую основную идею сбора мусора:

Everybody thinks about garbage collection the wrong way

Однако, - но это чистая спекуляция с информацией, представленной в ваших вопросах - там могут быть утечки памяти, связанные с методами расширения в вашем XSLT. Методы расширения могут привести к проблемам при перекомпиляции таблицы стилей каждый раз при преобразовании нового документа XML. Исправление прост: после компиляции кешируйте таблицу стилей.

1

Очень сложно сказать, что может быть проблемой. У меня был успех с использованием Ants Memory Profiler при исследовании проблем с памятью.

1

Вопреки мысли Я не думаю, что есть утечка памяти; Я бы profile память.

3

Какую меру вы используете для памяти? Существует множество возможных мер, и ни один из них не означает «используемая память». С виртуальной памятью, совместным использованием изображений и т. Д. Все не так просто.

даже тогда, когда находится в режиме ожидания он имеет память о 500MB

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

начал обработку сообщений от 12 МБ в размере

Если XML-документ раскладывается в объектной модели (например, XmlDocument) она требует много больше памяти (много ссылок на другие узлы). Либо посмотрите на другую модель (как XDocument, так и XPathDocument - более легкий вес) или, лучше, обработать XML с помощью XmlReader и, следовательно, никогда не иметь полностью расширенную объектную модель.

5

SIR! ПОЗВОНИТЕ ЗАДАЧУ МЕНЕДЖЕРА И ПРОГУЛКА. Шутки в сторону. Управление памятью в .NET не настроено на минимальный размер. Он настроен на эффективность. Он будет храниться в памяти, а не отпускать его обратно в систему. Не поддавайтесь соблазну прислушиваться к своей памяти, если нет реальной проблемы (исключения OOM, проблемы с системой).

2

Прежде всего, добавьте что-то в свою систему, чтобы вы могли вручную вызвать сбор мусора для целей расследования. CLR не обязательно возвратит память к O/S, если она не находится в дефиците. Вы должны использовать этот механизм для ручного вызова сборки мусора, когда у вас есть система в состоянии покоя, чтобы вы могли увидеть, падает ли память. (Вы должны дважды вызвать GC.Collect(2), чтобы объекты с финализаторами были фактически собраны, а не только финализированы.)

Если память падает до уровня покоя после полного GC, то у вас нет проблемы: просто это .NET не проявляя активности при освобождении памяти.

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

Одна вещь, которой нужно следить, это интернирование внутри строки: если вы вручную выполняете строки, это может привести к фрагментации LOH.

+0

делает компиляторы интернированием строковых литералов, вызывая такую ​​же фрагментацию? –

+0

@Matt Briggs: Нет, компилятор фактически не выполняет интернирование как таковое, вместо этого все строковые литералы скомпилируются таким образом, что при запуске программы будет создан только один строковый объект. –

0

Я проверил бы обработчики событий. Если вы не будете осторожно отделять их, когда закончите, кажется, что создавать ссылки на объекты, которые не будут собираться 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); 
    } 
}