2

I guess I'm have a serious memory leak on one of my C# executables (it's a console application). The memory size keeps on increasing and overtime I need to restart the application to bring the memory usage down. I'm using a FileSystemWatcher and then when the file is available picking it up converting it to utf-8 and forwarding it. And then write to the console that the file is processed at this time. So it writes to the console every time it processes it.

I'm using Ants memory profiler and am pretty fresh at starting to use it. I can't figure it out with it. When I take a memory snapshot, it shows me:

namespace:System, Classname: byte[] --- This increases every time it processes the file and displays it on the console (by 40,000 bytes) and never goes back.

Is this correct.

Update:

class Class1
{
    private static FileSystemWatcher watcher = new FileSystemWatcher();

    public static void Main()
    {
        WatchFile();
        Console.ReadLine();
    }

    private static void WatchFile()
    {
        watcher.Path = @"N:\";
        watcher.NotifyFilter = NotifyFilters.LastWrite;
        watcher.Filter = "*.xml";
        watcher.Changed += new FileSystemEventHandler(convert);
        watcher.Error += new ErrorEventHandler(WatcherError);
        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Press \'q\' to quit.");

        while (Console.Read() != 'q');
    }

    public static string CrL = "\r\n";

    private static object lockObject = new object();

    private static void convert(object source, FileSystemEventArgs f)
    {
       string FileName;
       FileName = f.FullPath;

       string destinationFile = @"F:\test.xml";

       Thread.Sleep(2000);
       if (!Monitor.TryEnter(lockObject))
       {
           return;
       }
       try
       {
           watcher.EnableRaisingEvents = false;
           Thread.Sleep(2000);

           var doc = new XmlDocument();
           doc.Load(FileName);

           XmlWriterSettings settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true };

           using (var writer = XmlWriter.Create(destinationFile, settings))
           {
               doc.Save(writer);
           }

           Console.WriteLine("File Copied" + "  " + DateTime.Now.ToString("HH:mm:ss tt")); 
           Console.WriteLine("Press \'q\' to quit."); 
           Console.Write(CrL);
       }
       catch (Exception e)
       {
           Console.WriteLine("The process failed: {0}", e.ToString());
       }
       finally
       {
           Monitor.Exit(lockObject);
           watcher.EnableRaisingEvents = true;
           GC.Collect();
           GC.WaitForPendingFinalizers();
           GC.Collect();
       }
    }

    private class Utf8StringWriter : StringWriter
    {
        public override Encoding Encoding { get { return Encoding.UTF8; } }
    }

    private static void WatcherError(object source, ErrorEventArgs e)
    {
        Exception watchException = e.GetException();
        watcher = new FileSystemWatcher();
        while (!watcher.EnableRaisingEvents)
        {
            try
            {
                WatchFile();
                Console.WriteLine("I'm Back!!");
            }
            catch
            {
                Thread.Sleep(2000);
            }
        }
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
user726720
  • 1,127
  • 7
  • 25
  • 59
  • 1
    What is you question? – Hamlet Hakobyan Mar 29 '13 at 06:04
  • 1
    Can you show the code of your filewatcher? It's kind of hard to say what's going on just by the description. – Tim Mar 29 '13 at 06:04
  • Edited to add the code – user726720 Mar 29 '13 at 06:14
  • @HamletHakobyan : I'm having a serious memory leak it leaks over time, and the console app needs to be restarted every 15-20 day, by the way I'm running 3 of these same apps for different folders – user726720 Mar 29 '13 at 06:16
  • you could try setting `doc = null;` in the `EventHandler` `convert` to release all references to it – jordanhill123 Mar 29 '13 at 06:21
  • Is there more code to your application than this? – jordanhill123 Mar 29 '13 at 06:24
  • @jordanhill123 : no that's the final code – user726720 Mar 29 '13 at 06:26
  • @jordanhill123 : should I set the doc=null in the finally or just after the using (XMLwriter.....). Why do we need to set doc=null shouldn't this be collected by the GC. – user726720 Mar 29 '13 at 06:28
  • Does your watchError handler trigger? From a first sight it can create infinite reqursion... – David Goshadze Mar 29 '13 at 06:39
  • @DavidGoshadze : I'm not sure if I get that. Can you please explain further. Thank you – user726720 Mar 29 '13 at 06:42
  • From error handler you are calling WatchFile() which installs new handler and if errorneous situation is still there it will trigger error handler and so on... – David Goshadze Mar 29 '13 at 06:46
  • @DavidGoshadze: Your concern is right, then what should I do to dispose the old handle. But this would only occur in case there was an error. I can see I never get that watcher error, because if i do it should do a console writeline 'I'm back'. Which I never get. Anyways how can I dispose the old handle. – user726720 Mar 29 '13 at 06:57
  • @AndreasNiedermair: I'm reading the xml file converting it utf-8 and then pushing it to the new destination – user726720 Mar 29 '13 at 08:00

2 Answers2

2

Try it like this:

var doc = new XmlDocument();
doc.Load(FileName);

XmlWriterSettings settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true };

using (var writer = XmlWriter.Create(destinationFile, settings))
{
    doc.Save(writer);
}
doc = null;

If you don't set doc to null, the GC may see it still as an active reference and not free the memory. The GC has its own logic for garbage collection and forcing a full garbage collection is not the best practice (Best Practice for Forcing Garbage Collection in C#).

Also I don't believe that you have a memory leak; it appears that the GC hasn't been triggered to free the memory. By what do you mean when you say the memory keeps on increasing? What level does it reach when you feel you need to bring it back down?

EDIT

See this link for options on reading XML files: Deciding on when to use XmlDocument vs XmlReader

When working with very large XML files, it possible to get XMLDocuments allocated on the large object heap(LOH) which can cause fragmentation in the LOH and lead to OutOfMemory Exceptions.

XMLReader may be a better fit for you here then.

Community
  • 1
  • 1
jordanhill123
  • 4,142
  • 2
  • 31
  • 40
  • Thanks for that, Yes I tried that. It's still the same. In the ants it's showing me the private bytes (byte[]) increase by 40,000 bytes everytime it processes the file and displays it on the console. – user726720 Mar 29 '13 at 06:48
  • Then how do you trigger the GC to free the memory. 3 of these apps left running to target different folders. It takes 15 days to fill up the 1gb of memory I have on the machine. So then the apps need a restart or the pc needs a restart to refresh – user726720 Mar 29 '13 at 06:49
  • Does the application give you out of memory exceptions or does the system slow noticeably? What do you use as the trigger for restarting the PC? – jordanhill123 Mar 29 '13 at 06:51
  • If left like that for more then 15 days the sytems gets a bit slow and the apps stop working giving me an exception with regards to the memory, I can't recall what was this exception. I had it a while ago until after I decided to restart it every 15 days – user726720 Mar 29 '13 at 06:53
  • I doubt that explicitly setting `doc` to `null` will work, as the variable will get collected at the end of the method automatically. So moving the call, which forces the GC to collect, to the outer method may suffice. –  Mar 29 '13 at 07:29
  • @AndreasNiedermair: You mean should I take GC.collect(); out of finally and put it where exactly, please explain – user726720 Mar 29 '13 at 07:41
  • Oh, I've just realized you've used `convert`-method as an eventHandler, so you'd need another wrapping of the inner copying-mechanism... –  Mar 29 '13 at 07:58
  • @AndreasNiedermair : I'm sorry I don't get that, can you please explain this as an answer. Thanks for your efforts – user726720 Mar 29 '13 at 07:59
  • @AndreasNiedermair I agree that explicitly setting `doc = null;` shouldn't need to be done; just didn't see anything else in his code causing a constant increase in memory usage, except for writes to the Console which should be negligible. – jordanhill123 Mar 29 '13 at 08:52
  • @jordanhill123 :) I am not sure how `lock`/`Monitor`, LOH and `Thread.Sleep` (is evil!) play together as well. –  Mar 29 '13 at 08:57
  • @AndreasNiedermair : Thanks for the code, this looks promising I will study and probably include that in my code. But currently the problem is resolved by installing a hotfix from microsoft for the filesystemwatcher. Probably that was the culprit. Couldn't figure this out through ants. Anyways, thanks to alll who have been trying to help me out. I have been scratching my head for months now over this code for memory leak. Finally it is resolved. THanks once again – user726720 Mar 29 '13 at 09:43
2

Similar question here - Memory leak while using Threads

From what I understand from the solution, writing to Console and using Thread.Sleep was the issue.

Also, maybe applicable (or not) - http://support.microsoft.com/kb/2628838

Download hotfix (Reliability Update 2 for Framework 4.0) file from here- http://support.microsoft.com/kb/2600217

Community
  • 1
  • 1
Arun
  • 969
  • 7
  • 17
  • yes, no new threads but there's still the writing to console though. And I think the issue was with using Threading and Thread.Sleep rather than creating new Threads (Answer Edited). At least that's my understanding. – Arun Mar 29 '13 at 07:16
  • thanks for the link for the hotfix.. Maybe this could work, but I'm not able to download this from the link. Is it possible you can point me to where I can download this from – user726720 Mar 29 '13 at 07:23
  • Updated answer with link. Did you try commenting out the Console.Write code and see if that helped ? – Arun Mar 29 '13 at 07:27
  • @Arun: Yes I'm testing that now without the write code. The leak has reduced considerably. I mean before it was 40,000bytes for every file processed (I can see that in ants) and now it's 16,000 bytes, everyime the file is processed. Yes thats a huge difference. Probably should I try with the hotfix. – user726720 Mar 29 '13 at 07:46
  • Hotfix is probably the best way to go if you are targeting Net Framework 4 – jordanhill123 Mar 29 '13 at 08:53
  • @Arun: It seems like the problem is resolved. I'm not getting any increase any more. Even though I'm using the console.write code again. This is resolved after I installed the hotfix. I will monitor this through out the day today to check this. But currently it seems okay. I enabled write code again. – user726720 Mar 29 '13 at 09:40