views:

1082

answers:

3

Hi all, I have a little problem with memory management in a Windows Service written in C# (framework 3.5, visual studio 2008).

The service run fine, with a Timer and a CallBack the fire the procedure every 3 minutes. Therefore, the memory in the Windows Task Manager slowly growing at every timer's run.

Have you an idea to how resolve this issue?

To simplify the problem, below is a very simple code that exibits the same problem:

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();

    }

    }
}

Any help will be appreciated!

stefano

+3  A: 

You're not disposing your FileStream. The garbage collector can call Dispose() for you, but it's non-deterministic (i.e. you don't know when/if it's going to happen). It's probably decided here not to bother. As a result, recommended best practice is to consider wrapping anything that implements IDisposable in using statements:

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(" *----------------*");
    }
}

For maintenance and DRY reasons, you should also consider refactoring the file-writing code to a separate method:

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");
}
Neil Barnwell
private void goEXE(Object state) { ... same code as above ... m_streamWriter.Dispose(); fs.Dispose(); }
stexcec
A: 

I would assume your file keeps growing since you're appending new lines to the end and thus streamwriter is consuming more memory.

I would rather create one stream writer at programm start up instead of doing it at the callback. But then you will have a file locked by your service.

Captain Comic
+1  A: 

Additionally, .NET's Garbage Collection runs when certain optimal parameters are hit. If the system isn't running low on memory, it may decide that running a GC.Collect is too expensive, and thus your non-referenced, but still in memory objects will continue to stay that way.

Gregory