2010-09-23 2 views
1

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

Служба сама по себе отлично работает, ну ничего особенного :), но когда я запускаю сервис, он использует около 1.6 МБ ОЗУ, и каждые 10 секунд он растет, как 60-70 тыс., Что является способом жить с ним. Я попытался уничтожить и очистить все ресурсы. Пробовал работать с System.Timers вместо фактического решения, но ничего действительно не работает, как я этого хочу, память все еще растет.

Нет разницы в отладке или выпустить версию, и я использую его на .Net 2, не знаю, если это сделать разницу вам 3,3.5 или 4.

Любой намек ?!

using System; 
using System.IO; 
using System.Diagnostics; 
using System.ServiceProcess; 
using System.Threading; 
using System.Timers; 

namespace Watchguard 
{ 
    class WindowsService : ServiceBase 
    { 

    Thread mWorker; 
    AutoResetEvent mStop = new AutoResetEvent(false); 

    /// <summary> 
    /// Public Constructor for WindowsService. 
    /// - Put all of your Initialization code here. 
    /// </summary> 
    public WindowsService() 
    { 
     this.ServiceName = "Informer Watchguard"; 
     this.EventLog.Source = "Informer Watchguard"; 
     this.EventLog.Log = "Application"; 

     // These Flags set whether or not to handle that specific 
     // type of event. Set to true if you need it, false otherwise. 
     this.CanHandlePowerEvent = false; 
     this.CanHandleSessionChangeEvent = false; 
     this.CanPauseAndContinue = false; 
     this.CanShutdown = false; 
     this.CanStop = true; 

     if (!EventLog.SourceExists("Informer Watchguard")) 
      EventLog.CreateEventSource("Informer Watchguard", "Application"); 
    } 

    /// <summary> 
    /// The Main Thread: This is where your Service is Run. 
    /// </summary> 
    static void Main() 
    { 
     ServiceBase.Run(new WindowsService()); 
    } 

    /// <summary> 
    /// Dispose of objects that need it here. 
    /// </summary> 
    /// <param name="disposing">Whether or not disposing is going on.</param> 
    protected override void Dispose(bool disposing) 
    { 
     base.Dispose(disposing); 
    } 

    /// <summary> 
    /// OnStart: Put startup code here 
    /// - Start threads, get inital data, etc. 
    /// </summary> 
    /// <param name="args"></param> 
    protected override void OnStart(string[] args) 
    { 

     base.OnStart(args); 

     MyLogEvent("Init"); 

     mWorker = new Thread(WatchServices); 
     mWorker.Start(); 

    } 

    /// <summary> 
    /// OnStop: Put your stop code here 
    /// - Stop threads, set final data, etc. 
    /// </summary> 
    protected override void OnStop() 
    { 

     mStop.Set(); 
     mWorker.Join(); 

     base.OnStop(); 

    } 

    /// <summary> 
    /// OnSessionChange(): To handle a change event from a Terminal Server session. 
    /// Useful if you need to determine when a user logs in remotely or logs off, 
    /// or when someone logs into the console. 
    /// </summary> 
    /// <param name="changeDescription"></param> 
    protected override void OnSessionChange(SessionChangeDescription changeDescription) 
    { 
     base.OnSessionChange(changeDescription); 
    } 

    private void WatchServices() 
    { 

     string scName = ""; 

     ServiceController[] scServices; 
     scServices = ServiceController.GetServices(); 

     for (; ;) 
     { 
     // Run this code once every 10 seconds or stop right away if the service is stopped 
     if (mStop.WaitOne(10000)) return; 
     // Do work... 
     foreach (ServiceController scTemp in scServices) 
     { 

      scName = scTemp.ServiceName.ToString().ToLower(); 

      if (scName == "InformerWatchguard") scName = ""; // don't do it for yourself 

      if (scName.Length > 8) scName = scName.Substring(0, 8); 

      if (scName == "informer") 
      { 

      ServiceController sc = new ServiceController(scTemp.ServiceName.ToString()); 

      if (sc.Status == ServiceControllerStatus.Stopped) 
      { 

       sc.Start(); 
       MyLogEvent("Found service " + scTemp.ServiceName.ToString() + " which has status: " + sc.Status + "\nRestarting Service..."); 

      } 

      sc.Dispose(); 
      sc = null; 

      } 
     } 
     } 

    } 

    private static void MyLogEvent(String Message) 
    { 
     // Create an eEventLog instance and assign its source. 
     EventLog myLog = new EventLog(); 
     myLog.Source = "Informer Watchguard"; 

     // Write an informational entry to the event log. 
     myLog.WriteEntry(Message); 
    } 
    } 
} 

ответ

1

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

if (scName == "informer") 
{ 
    try { 
     using(ServiceController sc = new ServiceController(scTemp.ServiceName.ToString())) { 
      if (sc.Status == ServiceControllerStatus.Stopped) 
      { 
       sc.Start(); 
       MyLogEvent("Found service " + scTemp.ServiceName.ToString() + " which has status: " + sc.Status + "\nRestarting Service..."); 
      } 
     } 
    } catch { 
     // Write debug log here 
    } 
} 

Вы можете удалить внешнюю TRY/поймать после того, как расследование, в результате чего using заявления, чтобы убедиться Dispose называется, даже если исключение брошено внутри.

+0

. Вам также может быть полезно прочитать ответ Джона Скита здесь http://stackoverflow.com/questions/574019/calling-null- на-класс-vs-распоряжаться, чтобы быть понятным при утилизации и обнулении. –

+0

Да, должно быть хорошей вещью справиться с исключениями, когда сервис начинается с любой стороны. Это также реализовано. – YvesR

1

Как минимум, вы должны сделать это в вашем коде регистрации, поскольку EventLog должен быть Dispose() d. Похоже, что этот ресурс можно было бы использовать повторно, а не new - при каждом вызове. Вы также можете рассмотреть using в своем основном цикле для объектов ServiceController, чтобы сделать код более безопасным.

private static void MyLogEvent(String Message) 
{ 
    // Create an eEventLog instance and assign its source. 
    using (EventLog myLog = new EventLog()) 
{ 
    myLog.Source = "Informer Watchguard"; 

    // Write an informational entry to the event log. 
    myLog.WriteEntry(Message); 
} 
} 
+0

Я внедрил ваше предложение и посмотрю, что он делает, спасибо! Поскольку я новичок в C# (обычно Ruby на rails developer :)), если я правильно понимаю, что use() делает утилиту автоматически после? – YvesR

+0

не только Dispose, он окружает блок с помощью try/finally, поэтому Dispose всегда называется обязательно. –

+0

@YvesR - есть. Это определенно будет протекать без вызова EventLog.Dispose() '. См. Http://msdn.microsoft.com/en-us/library/yh598w02.aspx –

0

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

ServiceController[] scServices = ServiceController.GetServices(); 

Вы также хотите, чтобы избавиться от вашей ссылки на EventLog и к экземплярам ServiceController. Как указывает Артем, следите за исключениями, которые мешают вам это делать.

Поскольку память растет каждые 10 секунд, она должна быть чем-то в вашей петле.

Если память поднимается независимо от того, записываете ли вы в EventLog, то это не основная проблема.

Используется ли когда-нибудь память? То есть, сборщик мусора вылетел через некоторое время? Вы можете проверить эффект GC, сделав GC.Collect() перед тем, как вернуться в сон (хотя я буду осторожнее использовать его в производстве).

+0

Я внедрил ваше предложение и посмотрю, что он делает, спасибо! – YvesR

+0

@ Yves: Стив прав ... если вы только когда-либо проверяете сервис с именем «информатор», вы можете просто сделать «новый ServiceController» («информатор») ', а не прокручивать все службы. –

0

Я не уверен, что я точно понимаю проблему. Является ли сервис, который вы собираетесь контролировать, всегда остается неизменным. Из вашего кода будет видно, что ответ «да», и если это так, вы можете просто создать экземпляр класса ServiceController, передающий имя службы конструктору. В вашей процедуре потока вы хотите продолжить цикл до тех пор, пока не будет выдан стоп, а вызов метода WaitOne возвращает логическое значение, поэтому цикл while представляется подходящим. В цикле while вы можете вызвать метод Refresh для экземпляра класса ServiceController, чтобы получить текущее состояние службы. Ведение журнала событий требует простого вызова одного из статических методов EventLog.WriteEntry, при минимальном прохождении вашего сообщения и источника «Informer Watchguard» Экземпляр ServiceController может быть удален при выходе из цикла в потоковой подпрограмме Все это означает, что вы создаете меньше объектов, которые необходимо удалить, и, следовательно, менее вероятно, что некоторые утечки ресурсов будут существовать.

0

Благодаря всем предложениям. Наконец, сервис стабилен с некоторыми изменениями.

@Steve: Я наблюдаю за многими службами, начинающимися с одного и того же имени «Информер ...», но я не знаю точно полных имен, поэтому я и так иду.