2009-11-12 6 views
2

J'ai un petit problème avec la gestion de la mémoire dans un service Windows écrit en C# (framework 3.5, visual studio 2008). Le service fonctionne bien, avec une minuterie et un rappel, la procédure est lancée toutes les 3 minutes. Par conséquent, la mémoire dans le Gestionnaire des tâches Windows augmente lentement à chaque exécution de la minuterie.Problème de service C# windows avec mémoire (fuite de mémoire?)

Avez-vous une idée de comment résoudre ce problème?

Pour simplifier le problème, ci-dessous est un code très simple qui exibits le même problème:

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

    } 

    } 
} 

Toute aide sera appréciée!

stefano

Répondre

6

Vous n'êtes pas votre disposition FileStream. Le garbage collector peut appeler le Dispose() pour vous, mais il est non-déterministe (c'est-à-dire que vous ne savez pas quand/si cela va arriver). Il est probablement décidé ici de ne pas déranger. En conséquence, recommandé la meilleure pratique consiste à considérer tout emballage qui implémente IDisposable dans using déclarations:

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

Pour l'entretien et DRY raisons, vous devriez également considérer la refactorisation du code de fichier écrit à une méthode distincte:

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"); 
} 
+0

private void goEXE (Object state) { ... même code comme ci-dessus ... m_streamWriter.Dispose(); fs.Dispose(); } – stexcec

+0

Nice vient de réparer une fuite de mémoire en utilisant un StreamWriter! THX! – Moss

+0

Pourquoi l'utilisation imbriquée sur StreamWriter? J'ai cherché autour mais je ne vois pas une réponse. – GaTechThomas

0

Je suppose que votre fichier ne cesse de croître depuis que vous ajoutez de nouvelles lignes à la fin et que streamwriter consomme plus de mémoire.

Je préfère créer un écrivain de flux au démarrage du programme au lieu de le faire au rappel. Mais alors vous aurez un fichier verrouillé par votre service.

1

En outre,. Garbage Collection .NET s'exécute lorsque certains paramètres optimaux sont atteints. Si le système ne manque pas de mémoire, il peut décider que l'exécution d'un GC.Collect est trop chère, et donc vos objets non référencés, mais toujours dans la mémoire continueront à rester ainsi.

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

    } 
} 
0

Utilisez File.AppendAllText(path, content); il est préférable, il va gérer toutes vos fuites de mémoire.