В основном у меня есть программа, которая при загрузке загружает список файлов (как FileInfo
) и для каждого файла в списке загружает XML-документ (как XDocument
).
Затем программа считывает данные из нее в класс контейнера (сохраняя как IEnumerables
), после чего XDocument
выходит за пределы области видимости.
Затем программа экспортирует данные из класса контейнера в базу данных. Однако после экспорта контейнерный класс выходит за пределы области видимости, сборщик мусора не очищает контейнерный класс, который, поскольку его сохранение как IEnumerable
, похоже, приводит к сохранению в памяти XDocument
(не уверен, что это является причиной, но диспетчер задач показывает, что память из XDocument
не освобождается).
По мере того, как программа перебирает несколько файлов, программа в конечном итоге бросает исключение из памяти. Для смягчения этого ive закончилось использование
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;
}