Запуск службы Windows для наблюдения за запущенной службой увеличивает объем памяти (утечка) - PullRequest
1 голос
/ 23 сентября 2010

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

Сам сервис отлично работает, ну ничего особенного :), но когда я запускаю сервис, он использует около 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);
    }
  }
}

Ответы [ 5 ]

1 голос
/ 23 сентября 2010

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

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 / catch после исследования, оставив оператор using, чтобы убедиться, что Dispose вызывается, даже если исключение выдается внутри.

1 голос
/ 23 сентября 2010

Как минимум, вам необходимо сделать это в коде регистрации, поскольку 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 голосов
/ 04 января 2011

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

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

0 голосов
/ 23 сентября 2010

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

0 голосов
/ 23 сентября 2010

Это должно быть перенесено в цикл, поскольку вы не хотите сохранять ссылку на старые дескрипторы службы в течение всего срока службы:

ServiceController[] scServices = ServiceController.GetServices();

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

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

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

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

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