基本上我有一个程序,当它开始加载文件列表(如FileInfo
)时,对于列表中的每个文件,它都会加载一个XML文档(如XDocument
)。
程序然后将数据从中读取到容器类中(存储为IEnumerables
),此时XDocument
超出范围。
然后程序将数据从容器类导出到数据库。在导出之后,容器类超出了范围,但是,垃圾收集器没有清除容器类,因为它存储为IEnumerable
,似乎导致XDocument
留在内存中(不确定这是否是原因,但任务管理器显示XDocument
的内存未被释放。
当程序循环遍历多个文件时,程序最终会抛出一个内存不足的异常。为了减轻这种情况,我最终使用了
System.GC.Collect();
在容器超出范围后强制垃圾收集器运行。这是有效的,但我的问题是:
XDocument
内存?感谢。
修改:代码示例:
容器类:
public IEnumerable<CustomClassOne> CustomClassOne { get; set; }
public IEnumerable<CustomClassTwo> CustomClassTwo { get; set; }
public IEnumerable<CustomClassThree> CustomClassThree { get; set; }
...
public IEnumerable<CustomClassNine> CustomClassNine { get; set; }
自定义类:
public long VariableOne { get; set; }
public int VariableTwo { get; set; }
public DateTime VariableThree { get; set; }
...
无论如何,这确实是基本结构。自定义类通过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;
}
忘了提这个位(不确定它是否相关),文件可以压缩为.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;
}
}
希望这是足够的信息。 感谢
修改:System.GC.Collect()
100%无法正常工作,有时程序似乎保留XDocument
,任何人都知道为什么会这样?< / p>
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;
}
答案 0 :(得分:9)
在某些情况下,强制手动垃圾收集似乎已经解决了你的问题,但是可以肯定的是,这并不比巧合更好。
您需要做的是停止猜测导致内存压力问题的原因,并找出确定的问题。
我在类似的情况下使用了JetBrains dotTrace非常好的效果 - 设置断点,触发探查器并浏览所有“实时”对象及其关系的视图。可以很容易地找到哪些对象仍然保留,以及它们保存的引用。
虽然我自己没有使用过,但许多人也推荐使用RedGate Ants Memory Profiler。
这两种工具都有免费试用,这应该足以解决您当前的问题。虽然,我强烈建议买一个或者另一个 - dotTrace为我节省了数十小时的内存问题,非常值得投资。
答案 1 :(得分:5)
您的代码对我来说并不坏看,我也没有看到任何强迫收集的原因。如果您的自定义类包含对XDocument的XElements的引用,那么GC将不会既不收集它们也不收集文档本身。如果其他东西持有对你的枚举的引用,那么它们也不会被收集。所以我真的很想看到你的自定义类定义及其填充方式。
答案 2 :(得分:2)
您对调用GC.Collect
的态度是正确的。需要调用此方法表明您的代码存在其他问题。
然而,你的陈述有一些让我觉得你对记忆的理解有点偏差。任务管理器是一个非常差的指标,表明你的程序实际使用了多少内存;分析器对于此任务要好得多。就内存而言,如果可以收集,GC将在需要时收集内存。
虽然这是一个措辞细节,但你会问“如何确保处理XDocument内存”。 Disposed 通常用于指代手动释放非托管资源,例如数据库连接或文件句柄; GC 收集内存。
现在试着回答实际的问题。引用您不释放的对象非常容易,尤其是在使用lambdas和LINQ时。键入IEnumerable
的内容特别容易出现这种情况,因为懒惰评估的LINQ函数几乎总是会引入您认为未使用的对象的引用。您省略的ReadXMLDocument
代码可能是开始查找的好地方。
另一种可能性是TomTom所建议的,你正在使用的数据库类可能存储了你自己没有想到的对象。
答案 3 :(得分:2)
如果处理的XML文件太大(大约500-800M),则不能使用XDocument(或XmlDocument),因为它会尝试将整个文档加载到内存中。请参阅此讨论:Does LINQ handle large XML files? Getting OutOfMemoryException
在这种情况下,您应该使用XStreamingElement Class并从中构建ContainerClass。
也许去64位进程会有所帮助,但最佳做法是始终使用端到端的流式传输。
答案 4 :(得分:2)
这不是真正的答案,更多的调查建议:如果GC.Collect没有帮助,那意味着你仍然在某处保留对象的引用。 寻找可能保持参考的单身人士和缓存。
如果您真实地获得异常或者可以收集内存转储,您可以使用WinDbg + Sos查找谁拥有对象的引用:搜索“memory leaks sos windbg”以查找详细信息。
答案 5 :(得分:2)
无论如何,请使用
String.Equals(fileToRead.Extension, ".gz", StringComparison.OrdinalIgnoreCase)
代替
String.Compare()
答案 6 :(得分:0)
您可以尝试使用tolist强制进行评估:
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)).ToList();
return containerClass;
}
答案 7 :(得分:-1)
程序然后从中导出数据 容器类到数据库。 导出容器类之后 然而,超出了范围 垃圾收集器没有清理 容器类,因为它 存储为IEnumerable,似乎领先 留在记忆中的XDocument (不确定这是否是原因,但是 任务管理器正在显示内存 来自XDocument没有被释放)。
原因是LYNC存储了每个项目读取它自己的事务参考池。 Basiaclly它这样做,因此重读它可以使项目独特。
建议:
仅将基本键加载到数组中。提交。
一次一个地遍历列表和处理项目,每个项目后都会提交。