【问题标题】:c# windows service problem with memory (memory leak?)c# windows service 内存问题(内存泄漏?)
【发布时间】:2010-12-15 19:39:37
【问题描述】:

我在用 C#(框架 3.5,Visual Studio 2008)编写的 Windows 服务中的内存管理方面遇到了一点问题。

服务运行良好,每 3 分钟使用 Timer 和 CallBack 触发程序。 因此,Windows 任务管理器中的内存在每次计时器运行时都会缓慢增长。

您知道如何解决此问题吗?

为了简化问题,下面是一个非常简单的代码,可以解决同样的问题:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.IO;

namespace svcTest
{
public partial class svcTest : ServiceBase
{

    private Timer tmr;
    private TimerCallback tmrCallBack;

    public svcTest()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        FileStream fs = new FileStream(@"c:\svclog.txt", FileMode.OpenOrCreate, FileAccess.Write);
        StreamWriter m_streamWriter = new StreamWriter(fs);
        m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
        m_streamWriter.WriteLine("Service Started on " + DateTime.Now.ToLongDateString() + " at " + DateTime.Now.ToLongTimeString());
        m_streamWriter.WriteLine(" *----------------*");
        m_streamWriter.Flush();
        m_streamWriter.Close();

        tmrCallBack = new TimerCallback(goEXE);
        tmr = new Timer(tmrCallBack, null, 0, 1000 * 60 * 1 / 2);
    }

    protected override void OnStop()
    {
        FileStream fs = new FileStream(@"c:\svclog.txt", FileMode.OpenOrCreate, FileAccess.Write);
        StreamWriter m_streamWriter = new StreamWriter(fs);
        m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
        m_streamWriter.WriteLine("Service Stopped on " + DateTime.Now.ToLongDateString() + " at " + DateTime.Now.ToLongTimeString());
        m_streamWriter.WriteLine(" *----------------*");
        m_streamWriter.Flush();
        m_streamWriter.Close();

        tmr.Dispose();
    }

    private void goEXE(Object state)
    {
        Console.WriteLine(DateTime.Now.ToString());

        FileStream fs = new FileStream(@"c:\svclog.txt", FileMode.OpenOrCreate, FileAccess.Write);
        StreamWriter m_streamWriter = new StreamWriter(fs);
        m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
        m_streamWriter.WriteLine("Service running on " + DateTime.Now.ToLongDateString() + " at " + DateTime.Now.ToLongTimeString());
        m_streamWriter.WriteLine(" *----------------*");
        m_streamWriter.Flush();
        m_streamWriter.Close();

    }

    }
}

任何帮助将不胜感激!

斯特凡诺

【问题讨论】:

    标签: c# memory memory-leaks timer


    【解决方案1】:

    你没有处理你的FileStream。垃圾收集器可以为您调用Dispose(),但它是不确定的(即您不知道它何时/是否会发生)。它可能决定在这里不打扰。因此,推荐的最佳做法是考虑将任何实现 IDisposable 的内容包装在 using 语句中:

    using (FileStream fs = new FileStream(@"c:\svclog.txt", FileMode.OpenOrCreate, FileAccess.Write)
    {
        using (using (StreamWriter m_streamWriter = new StreamWriter(fs)))
        {
            m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
            m_streamWriter.WriteLine("Service Started on " + DateTime.Now.ToLongDateString() + " at " + DateTime.Now.ToLongTimeString());
            m_streamWriter.WriteLine(" *----------------*");
        }
    }
    

    出于维护和DRY 的原因,您还应该考虑将文件编写代码重构为单独的方法:

    private void Log(string message)
    {
        using (FileStream fs = new FileStream(@"c:\svclog.txt", FileMode.OpenOrCreate, FileAccess.Write)
        {
            using (using (StreamWriter m_streamWriter = new StreamWriter(fs)))
            {
                m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
                m_streamWriter.WriteLine(message + " " + DateTime.Now.ToLongDateString() + " at " + DateTime.Now.ToLongTimeString());
                m_streamWriter.WriteLine(" *----------------*");
            }
        }
    }
    
    protected override void OnStart(string[] args)
    {
        Log("Service Started");
    
        tmrCallBack = new TimerCallback(goEXE);
        tmr = new Timer(tmrCallBack, null, 0, 1000 * 60 * 1 / 2);
    }
    
    protected override void OnStop()
    {
        Log("Service Stopped");
    
        tmr.Dispose();
    }
    
    private void goEXE(Object state)
    {
        Console.WriteLine(DateTime.Now.ToString());
    
        Log("Service running");
    }
    

    【讨论】:

    • private void goEXE(Object state) { ... 与上面相同的代码 ... m_streamWriter.Dispose(); fs.Dispose(); }
    • 很好,刚刚使用 StreamWriter 修复了内存泄漏!谢谢!
    • 为什么要在 StreamWriter 上嵌套使用?我四处寻找,但没有看到答案。
    • 仅仅因为它实现了IDisposable 处置一次性物品是最佳实践。
    • 您可以将其设为私有字段或其他东西并保留它,仅在父对象本身被处置时才处置它,但这并不是这个答案的真正重点。
    【解决方案2】:

    我会假设您的文件不断增长,因为您正在追加新行 到最后,因此流写入器正在消耗更多内存。

    我宁愿在程序启动时创建一个流编写器 而不是在回调中进行。但是你会有一个文件被锁定 通过您的服务。

    【讨论】:

      【解决方案3】:

      此外,当达到某些最佳参数时,.NET 的垃圾收集就会运行。如果系统内存不足,它可能会认为运行 GC.Collect 成本太高,因此您的未引用但仍在内存中的对象将继续保持这种状态。

      【讨论】:

        【解决方案4】:
        public class Logger : IDisposable
        {
            public void Log(string message)
            {
                using (FileStream fs = new FileStream(@"c:\svclog.txt", FileMode.OpenOrCreate, FileAccess.Write)
                {
                    using (using (StreamWriter m_streamWriter = new StreamWriter(fs)))
                    {
                        m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
                        m_streamWriter.WriteLine(message + " " + DateTime.Now.ToLongDateString() + " at " + DateTime.Now.ToLongTimeString());
                        m_streamWriter.WriteLine(" *----------------*");
                    }
                }
            }
            public void Dispose()
            {
                GC.SuppressFinalize(this);
        
            }
        }
        

        【讨论】:

          【解决方案5】:

          最好使用File.AppendAllText(path, content);,它会处理你所有的内存泄漏。

          【讨论】:

            猜你喜欢
            • 2011-05-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-11-10
            • 1970-01-01
            • 2016-07-28
            相关资源
            最近更新 更多