2

我想我的一个 C# 可执行文件(它是一个控制台应用程序)存在严重的内存泄漏。内存大小不断增加,超时我需要重新启动应用程序以降低内存使用量。我正在使用 FileSystemWatcher,然后当文件可用时将其转换为 utf-8 并转发它。然后向控制台写入此时正在处理该文件。所以它每次处理它时都会写入控制台。

我正在使用 Ants 内存分析器,并且在开始使用它时非常新鲜。我无法弄清楚它。当我拍摄内存快照时,它会显示:

namespace:System, Classname: byte[] --- 每次处理文件并将其显示在控制台上时都会增加(增加 40,000 字节),并且永远不会返回。

它是否正确。

更新:

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);
            }
        }
    }
}
4

2 回答 2

2

试试这样:

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;

如果您不将 doc 设置为 null,GC 可能仍将其视为活动引用并且不会释放内存。GC 有自己的垃圾回收逻辑,强制进行完整的垃圾回收并不是最佳实践(Best Practice for Forcing Garbage Collection in C#)。

另外我不相信你有内存泄漏;似乎没有触发 GC 来释放内存。当您说内存不断增加时,您的意思是什么?当你觉得你需要把它降下来时,它会达到什么水平?

编辑

有关读取 XML 文件的选项,请参阅此链接:Decising on when to use XmlDocument vs XmlReader

在处理非常大的 XML 文件时,可能会在大对象堆 (LOH) 上分配 XMLDocuments,这可能会导致 LOH 中的碎片并导致 OutOfMemory 异常。

XMLReader那么这里可能更适合你。

于 2013-03-29T06:42:23.373 回答
2

类似的问题 - 使用线程时的内存泄漏

根据我对解决方案的了解,写入控制台并使用 Thread.Sleep 是问题所在。

此外,可能适用(或不适用) - http://support.microsoft.com/kb/2628838

从此处下载修补程序(Framework 4.0 的可靠性更新 2)文件 - http://support.microsoft.com/kb/2600217

于 2013-03-29T07:01:07.950 回答