运行Windows服务来监视服务运行增长内存(泄漏)

时间:2010-09-23 14:37:51

标签: c# windows memory service memory-leaks

我已在此处查看了所有帖子,但到目前为止找不到我的解决方案。 我确实设置了一个小服务,只应该监视我想要监视的其他服务是否运行,如果没有,请再次启动它并在应用程序事件日志中发送消息。

服务本身效果很好,没有什么特别的:),但是当我启动服务时,它使用大约1.6MB的RAM,并且每10秒就会增长到60-70k,这是很容易接受的。 我试过处理并清除所有资源。尝试使用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 个答案:

答案 0 :(得分: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);
 }
}

答案 1 :(得分: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 / catch,留下using语句以确保Dispose在被调用的情况下被调用。

答案 2 :(得分:0)

这应该移到循环中,因为您不希望在服务的整个过程中保留对旧服务句柄的引用:

ServiceController[] scServices = ServiceController.GetServices();

您还希望处理对EventLogServiceController实例的引用。正如Artem所指出的那样,要注意阻止你这样做的例外情况。

由于内存每10秒钟就会上升,所以它必须在你的循环中。

如果内存上升,无论你是否写入EventLog,那么这不是主要问题。

使用的内存是否会降低?即一段时间后垃圾收集器开始了吗?您可以在回到睡眠状态之前执行GC.Collect()来测试GC的效果(尽管我会小心在生产中使用它)。

答案 3 :(得分:0)

我不确定我是否完全理解这个问题。您要监控的服务是否始终相同。从您的代码中可以看出答案是肯定的,如果是这种情况,那么您可以简单地创建ServiceController类实例,将服务名称传递给构造函数。   在你的线程例程中,你想继续循环,直到发出一个停止,并且WaitOne方法调用返回一个布尔值,所以while循环似乎是合适的。在while循环中,您可以在ServiceController类实例上调用Refresh方法以获取服务的当前状态。   事件记录应该简单地要求调用一个静态方法EventLog.WriteEntry方法,至少传递你的消息和源'Informer Watchguard'   当您从线程例程中的循环退出时,可以处理ServiceController实例   所有这些都意味着您创建的对象需要更少,因此不太可能存在某些资源泄漏。

答案 4 :(得分:0)

感谢所有建议。 最后,服务现在稳定了一些修改。

@Steve:我看到很多服务都以同名“Informer ......”开头,但我不知道全名,这就是我这样做的原因。