В основном у меня есть программа, которая при запуске загружает список файлов (как 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;
}