XDocument + IEnumerable导致System.Xml.Linq.dll中出现内存不足异常

Man*_*rin 13 c# linq garbage-collection out-of-memory linq-to-xml

基本上我有一个程序,当它开始加载文件列表(as FileInfo)时,对于列表中的每个文件,它加载一个XML文档(如XDocument).

然后程序将数据从其中读取到容器类(存储为IEnumerables),此时XDocument超出范围.

然后程序将数据从容器类导出到数据库.导出后容器类超出范围,然而,垃圾收集器没有清理容器类,因为它的存储IEnumerable,似乎导致XDocument留在内存中(不确定这是否是原因,但任务经理正在显示XDocument未被释放的记忆.

当程序循环遍历多个文件时,程序最终会抛出一个内存不足的异常.为了减轻这种情况,我最终使用了

System.GC.Collect(); 
Run Code Online (Sandbox Code Playgroud)

在容器超出范围后强制垃圾收集器运行.这是有效的,但我的问题是:

  • 这是正确的做法吗?(强制垃圾收集器运行似乎有点奇怪)
  • 有没有更好的方法来确保处理XDocument内存?
  • 可能有一个不同的原因,除了IEnumerable,文件内存没有被释放?

谢谢.


编辑:代码示例:

  • 集装箱类:

    public IEnumerable<CustomClassOne> CustomClassOne { get; set; }
    public IEnumerable<CustomClassTwo> CustomClassTwo { get; set; }
    public IEnumerable<CustomClassThree> CustomClassThree { get; set; }
    ...
    public IEnumerable<CustomClassNine> CustomClassNine { get; set; }
    
    Run Code Online (Sandbox Code Playgroud)
  • 自定义类:

    public long VariableOne { get; set; }
    public int VariableTwo { get; set; }
    public DateTime VariableThree { get; set; }
    ...
    
    Run Code Online (Sandbox Code Playgroud)

    无论如何,这确实是基本结构.自定义类通过XML文档中的容器类填充.填充的结构本身使用非常少的内存.

从一个XML文档填充容器类,超出范围,然后加载下一个文档,例如

    public static void ExportAll(IEnumerable<FileInfo> files)
    {
        foreach (FileInfo file in files)
        {
            ExportFile(file);
            //Temporary to clear memory
            System.GC.Collect();
        }
    }
    private static void ExportFile(FileInfo file)
    {
        ContainerClass containerClass = Reader.ReadXMLDocument(file);
        ExportContainerClass(containerClass);
        //Export simply dumps the data from the container class into a database
        //Container Class (and any passed container classes) goes out of scope at end of export
    }

    public static ContainerClass ReadXMLDocument(FileInfo fileToRead)
    {
        XDocument document = GetXDocument(fileToRead);
        var containerClass = new ContainerClass();

        //ForEach customClass in containerClass
        //Read all data for customClass from XDocument

        return containerClass;
    }
Run Code Online (Sandbox Code Playgroud)

忘了提这个位(不确定它是否相关),文件可以压缩为.gz所以我有GetXDocument()方法加载它

    private static XDocument GetXDocument(FileInfo fileToRead)
    {
        XDocument document;

        using (FileStream fileStream = new FileStream(fileToRead.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            if (String.Equals(fileToRead.Extension, ".gz", StringComparison.OrdinalIgnoreCase))
            {
                using (GZipStream zipStream = new GZipStream(fileStream, CompressionMode.Decompress))
                {
                    document = XDocument.Load(zipStream);
                }
            }
            else
            {
                document = XDocument.Load(fileStream);
            }
            return document;
        }
    }
Run Code Online (Sandbox Code Playgroud)

希望这是足够的信息.谢谢

编辑:System.GC.Collect()不工作100%的时间,有时程序似乎保留XDocument,任何人有任何想法,这可能是为什么?

public static ContainerClass ReadXMLDocument(FileInfo fileToRead)
{
    XDocument document = GetXDocument(fileToRead);
    var containerClass = new ContainerClass();

    //ForEach customClass in containerClass
    //Read all data for customClass from XDocument

    containerClass.CustomClassOne = document.Descendants(ElementName)
        .DescendantsAndSelf(ElementChildName)
        .Select(a => ExtractDetails(a));

    return containerClass;
}

private static CustomClassOne ExtractDetails(XElement itemElement)
{
    var customClassOne = new CustomClassOne();
    customClassOne.VariableOne = Int64.Parse(itemElement.Attribute("id").Value.Substring(4));
    customClassOne.VariableTwo = int.Parse(itemElement.Element(osgb + "version").Value);
    customClassOne.VariableThree = DateTime.ParseExact(itemElement.Element(osgb + "versionDate").Value,
            "yyyy-MM-dd", CultureInfo.InvariantCulture);
    return customClassOne;
}
Run Code Online (Sandbox Code Playgroud)

Bev*_*van 9

在某些情况下,强制手动垃圾收集似乎已经解决了你的问题,但是可以肯定的是,这并不比巧合更好.

你需要做的是停止猜测是什么导致了你的记忆压力问题,而是要确定.

我在类似的情况下使用了JetBrains dotTrace非常好的效果 - 设置断点,触发探查器并浏览所有"实时"对象及其关系的视图.可以轻松找到仍保留哪些对象,以及它们保存的引用.

虽然我自己没有使用它,但许多人也推荐使用RedGate Ants Memory Profiler.

这两种工具都有免费试用,这应该足以解决您当前的问题.虽然,我强烈建议购买其中一个 - dotTrace为我节省了数十小时的内存问题,这是一个非常有价值的投资回报率.


Sch*_*999 5

你的代码对我来说并不坏看,我也没有看到任何强迫收集的原因.如果您的自定义类包含对XDocument的XElements的引用,那么GC将不会既不收集它们也不收集文档本身.如果其他东西持有对你的枚举的引用,那么它们也不会被收集.所以我真的很想看到你的自定义类定义以及它是如何填充的.