1

У меня есть служба Windows C#, работающая на платформе .NET Framework 3.5, которая демонстрирует постоянно растущее число GC-ручек (с использованием System Monitor на Windows Server 2003).В чем причина постоянной работы службы Windows .NET с номером Number (#) GC Handles?

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

«Большой размер кучи объекта» и «# байты во всех кучах» являются сравнительно статичными, и я вижу, что «% время в GC» показывает, что коллекции мусора происходят.

Счетчик «Частных байтов» также увеличивается.

Этот симптом заставляет мое «использование памяти» в диспетчере задач расти со скоростью около 35 МБ в день, что неприемлемо, так как Служба в основном запускает простой запрос SELECT против Oracle 10g и использует .NET TraceSources каждые 5 секунд. Вероятно, стоит упомянуть, что TraceSource выводит в журнал событий Windows и текстовый файл, используя объекты .NET Listeners.

Кто-нибудь знает, почему «# GC Handles» постоянно растет, так как я считаю, что это связано с моим увеличением «использования памяти»?

+0

Используйте windbg + sos, чтобы определить, какие ресурсы просочились. Подробнее см. Http://social.msdn.microsoft.com/Forums/en-US/clr/thread/61331e3a-0f35-48cc-a9e4-8450a445abb6/. – Constantin

ответ

2

OK - отсортировано.

После изучения исходного кода для класса .NET TraceSource выяснилось, что по дизайну он имеет 2 списка WeakReferences - 1 для объектов TraceSource и один для коммутаторов.

Он делает это для поддержки метода RefreshAll.

Мне пришлось использовать Reflection, чтобы вручную очистить эти списки. Таким образом, я не могу использовать метод RefreshAll безопасно, но по крайней мере у меня больше нет в Private Bytes и GC Handles.

Чтобы воспроизвести проблему, просто создайте всю нагрузку объектов TraceSource и закройте их - вы увидите «утечку».

+0

F.Y.I. В настоящее время у меня есть открытая версия MSDN с этой проблемой, и я попытаюсь выяснить, что такое fix/workaround. – 2009-03-02 08:46:39

+0

Получено обходное решение от Microsoft. Если кому-то нужно знать, как обойти это, дайте мне знать ... – 2009-03-12 08:35:28

+0

Да! Я был бы очень заинтересован в этом обходном пути, поскольку мы сильно используем TraceSources. Не возражаете ли вы опубликовать его здесь? Благодаря! – Alex

1

Вы не выпускаете неуправляемые ресурсы, на которые ссылается ваш код правильно.

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

var handle = System.Runtime.InteropServices.GCHandle.Alloc(myObject, 
     System.Runtime.InteropServices.GCHandleType.Pinned); 

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

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

0

Вы используете ODP.Net? Помните, что ODP.Net не полностью управляется и имеет собственный компонент. Используете ли вы какие-либо другие собственные ресурсы, например ActiveDirectory и т. Д. Помните, что даже если у вас есть .Net-сборки, это тонкий слой поверх COM для таких вещей, как AD. Я видел массивные утечки памяти в коде AD, который я использовал. У меня не было проблемы с памятью с ODP.Net, хотя корпоративная служба моей компании работает с ODP.Net.

+0

Насколько я знаю, мы явно не используем ODP.NET. Мы используем все, что установлено с Visual Studio .NET 2008 и Oracle Oracle 10.2.01 Oracle – 2009-02-25 09:20:14

+0

System.Data.OracleClient 2.0.0.0 – 2009-02-25 09:21:04

1

Для чего это стоит, мы исправили эту проблему в .NET 4.0 для TraceSource и Switch.

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

+0

Hi Matt, Спасибо за обновление. Я думаю, что единственная проблема заключается в том, что я не могу найти в любой документации Microsoft, что это «лучшая практика, чтобы не создавать кучу разных экземпляров TraceSource». – 2009-04-15 08:13:21

+0

Наверное и недосмотр с нашей стороны. Я буду работать с документацией, чтобы получить что-то вроде этого, добавленного в MSDN. –

0
private void TidyWeakReferences(System.Diagnostics.TraceSource source) { 

     try { 
      // Clear down the Switch's WeakReferences 
      TidyList(source, typeof(System.Diagnostics.Switch).GetFields(BindingFlags.NonPublic | BindingFlags.Static)); 
      // Clear down the Source's WeakReferences 
      TidyList(source, typeof(System.Diagnostics.TraceSource).GetFields(BindingFlags.NonPublic | BindingFlags.Static)); 
     } 
     catch { /* Nothing we can do here */ } 
    } 

private void TidyList(System.Diagnostics.TraceSource source, FieldInfo[] info) { 

     List<WeakReference> list = null; 

     foreach (FieldInfo fi in info) { 

      if (fi.Name == "switches" | fi.Name == "tracesources") { 

       list = (List<WeakReference>)fi.GetValue(null); 
       lock (list) { 
        for (int i = list.Count - 1; i >= 0; i--) { 
         // Check to see if the GC has already collected these objects 
         if (!list[i].IsAlive) { 
          // It's dead, so remove it from the List (as .NET Framework 4.0 SHOULD fix.) 
          list.RemoveAt(i); 
         } 
        } 
       } 
      } 
     } 
0

Эта ссылка на опыт отладки должна быть добавлена ​​к обсуждению. Он дополняет обходное решение, опубликованное @thehowler.

http://www.wintellect.com/blogs/jrobbins/is-that-a-weakreference-in-your-gen-2-or-are-you-just-glad-to-see-me

В принципе, каждое новое выделение из TraceSource добавляет к списку WeakReference, который не мусора.