【发布时间】:2011-05-26 01:16:11
【问题描述】:
基本上我有一个程序,它在启动时加载文件列表(如FileInfo),并为列表中的每个文件加载一个XML文档(如XDocument)。
然后程序将其中的数据读入容器类(存储为IEnumerables),此时XDocument 超出范围。
然后程序将数据从容器类导出到数据库。但是,在导出容器类超出范围后,垃圾收集器没有清理容器类,因为它存储为IEnumerable,似乎导致XDocument 留在内存中(不确定这是否是原因,但任务管理器显示来自XDocument 的内存没有被释放)。
由于程序循环遍历多个文件,最终程序会抛出内存不足异常。为了缓解这种情况,我最终使用了
System.GC.Collect();
在容器超出范围后强制垃圾收集器运行。这是有效的,但我的问题是:
- 这是正确的做法吗? (强制垃圾收集器运行似乎有点奇怪)
- 有没有更好的方法来确保
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; } -
自定义类:
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,有人知道为什么会这样吗?
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;
}
【问题讨论】:
-
我建议不要手动调用 GC。我们可以看一些示例代码,以便我们了解它在做什么吗?
-
我建议您认为事情不会超出范围。您的 IEnumerable 将引用(以支持延迟评估)您不再直接在其他地方引用的内容。只要您引用 IEnumerable,它枚举的所有内容都仍然存在。
-
@Matthew:明天将添加一些示例代码,因为我目前无法访问它。 @Colin:如果我认为手动调用垃圾收集器时它没有超出范围,肯定不会做任何事情?
-
@Manatherin:另外,请记住
IEnumerable<T>继承IDisposable,因此您可能希望在这些对象上调用Dispose,或者将它们放入using块中。跨度> -
@John:实际上
IEnumerable<T>没有实现IDisposable,但IEnumerator<T>实现了。除非他直接调用 GetEnumerator,否则没有什么可处置的。
标签: c# linq garbage-collection linq-to-xml out-of-memory