Утечка памяти с использованием StreamReader и XmlSerializer
Я занимаюсь поиском в течение нескольких последних нескольких часов и пробую разные вещи, но не могу понять этого.
Когда я запускаю этот код, использование памяти постоянно растет.
while (true)
{
try
{
foreach (string sym in stringlist)
{
StreamReader r = new StreamReader(@"C:\Program Files\" + sym + ".xml");
XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"));
XMLObj obj = (XMLObj)xml.Deserialize(r);
obj.Dispose();
r.Dispose();
r.Close();
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
Thread.Sleep(1000);
Console.Clear();
}
XMLObj - это настраиваемый объект
[Serializable()]
public class XMLObj: IDisposable
{
[XmlElement("block")]
public List<XMLnode> nodes{ get; set; }
public XMLObj() { }
public void Dispose()
{
nodes.ForEach(n => n.Dispose());
nodes= null;
GC.SuppressFinalize(this);
}
}
Я попытался добавить в GC.Collect(); но это ничего не делает.
Ответы
Ответ 1
Утечка здесь:
new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"))
XmlSerializer
использует сборку, и сборки не могут быть собраны. Он выполняет некоторый автоматический кеш/повторное использование для простейших сценариев конструктора (new XmlSerializer(Type)
и т.д.), Но не для этого сценария. Следовательно, вы должны кэшировать его вручную:
static readonly XmlSerializer mySerializer =
new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"))
и используйте экземпляр кэшированного serializer.
Ответ 2
Во-первых, вы должны избавиться от своего StreamReader, даже если выбрано исключение (то же самое для XMLObj). Используйте оператор using
. В настоящее время вы не будете уничтожать, когда генерируется исключение.
Очень маловероятно, что у вас есть утечка памяти. Скорее всего, время выполнения просто не собиралось собирать память. Даже GC.Collect не обязательно приведет к освобождению памяти.
Я столкнулся с подобными ситуациями при обработке очень больших файлов XML (multi-GB). Несмотря на то, что время выполнения захватывает большую часть доступной памяти, оно освобождает его, когда гарантируется давление в памяти.
Вы можете использовать профилировщик памяти в Visual Studio, чтобы узнать, какая память выделена, и в каком поколении он находится.
UPDATE
Комментарий от @KaiEichinger стоит исследовать. Это указывает на то, что XmlSerializer может создавать новое определение кэшированного объекта для каждой итерации цикла
Конструктор XMLSerializer создает временную сборку для типа, который будет сериализован с использованием отражения, и поскольку генерация кода дорога, сборка кэшируется в памяти на основе типа. Но много раз имя корня будет изменено и может быть динамическим, и оно не будет кэшировать динамическую сборку. Поэтому всякий раз, когда вызывается вышеуказанная строка кода, она каждый раз загружает новую сборку и остается в памяти до тех пор, пока AppDomain не будет выгружен.
Ответ 3
Из MSDN: введите описание ссылки здесь
Чтобы повысить производительность, инфраструктура XML-сериализации динамически генерирует сборки для сериализации и десериализации определенных типов. Инфраструктура находит и повторно использует эти сборки. Это происходит только при использовании следующих конструкторов:
XmlSerializer.XmlSerializer(тип)
XmlSerializer.XmlSerializer(Type, String)
Если вы используете любой из других конструкторов, генерируются и не разгружаются несколько версий одной и той же сборки, что приводит к утечке памяти и низкой производительности. Самое простое решение - использовать один из ранее упомянутых двух конструкторов. В противном случае вы должны кэшировать сборки в Hashtable, как показано в следующем примере.
= > Итак, чтобы исправить это, вы должны использовать этот конструктор XmlSerializer xml = new XmlSerializer(typeof(XMLObj))
вместо XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"));
и добавьте корневой XML-атрибут в класс XMLObj.
[Serializable()]
[XmlRoot("root")]
public class XMLObj: IDisposable
{
[XmlElement("block")]
public List<XMLnode> nodes{ get; set; }
public XMLObj() { }
public void Dispose()
{
nodes.ForEach(n => n.Dispose());
nodes= null;
GC.SuppressFinalize(this);
}
}
Ответ 4
Я использую класс "cache", чтобы избежать создания экземпляра xmlserializer каждый раз, когда вам нужно сериализовать что-либо (также добавлен XmlCommentAttribute для добавления комментариев к сериализованным свойствам в выводе xml), для меня это работает как sharm, надеюсь, помощь кто-то с этим:
public static class XmlSerializerCache
{
private static object Locker = new object();
private static Dictionary<string, XmlSerializer> SerializerCacheForUtils = new Dictionary<string, XmlSerializer>();
public static XmlSerializer GetSerializer<T>()
{
return GetSerializer<T>(null);
}
public static XmlSerializer GetSerializer<T>(Type[] ExtraTypes)
{
return GetSerializer(typeof(T), ExtraTypes);
}
public static XmlSerializer GetSerializer(Type MainTypeForSerialization)
{
return GetSerializer(MainTypeForSerialization, null);
}
public static XmlSerializer GetSerializer(Type MainTypeForSerialization, Type[] ExtraTypes)
{
string Signature = MainTypeForSerialization.FullName;
if (ExtraTypes != null)
{
foreach (Type Tp in ExtraTypes)
Signature += "-" + Tp.FullName;
}
XmlSerializer XmlEventSerializer;
if (SerializerCacheForUtils.ContainsKey(Signature))
XmlEventSerializer = SerializerCacheForUtils[Signature];
else
{
if (ExtraTypes == null)
XmlEventSerializer = new XmlSerializer(MainTypeForSerialization);
else
XmlEventSerializer = new XmlSerializer(MainTypeForSerialization, ExtraTypes);
SerializerCacheForUtils.Add(Signature, XmlEventSerializer);
}
return XmlEventSerializer;
}
public static T Deserialize<T>(XDocument XmlData)
{
return Deserialize<T>(XmlData, null);
}
public static T Deserialize<T>(XDocument XmlData, Type[] ExtraTypes)
{
lock (Locker)
{
T Result = default(T);
try
{
XmlReader XmlReader = XmlData.Root.CreateReader();
XmlSerializer Ser = GetSerializer<T>(ExtraTypes);
Result = (T)Ser.Deserialize(XmlReader);
XmlReader.Dispose();
return Result;
}
catch (Exception Ex)
{
throw new Exception("Could not deserialize to " + typeof(T).Name, Ex);
}
}
}
public static T Deserialize<T>(string XmlData)
{
return Deserialize<T>(XmlData, null);
}
public static T Deserialize<T>(string XmlData, Type[] ExtraTypes)
{
lock (Locker)
{
T Result = default(T);
try
{
using (MemoryStream Stream = new MemoryStream())
{
using (StreamWriter Writer = new StreamWriter(Stream))
{
Writer.Write(XmlData);
Writer.Flush();
Stream.Position = 0;
XmlSerializer Ser = GetSerializer<T>(ExtraTypes);
Result = (T)Ser.Deserialize(Stream);
Writer.Close();
}
}
return Result;
}
catch (Exception Ex)
{
throw new Exception("Could not deserialize to " + typeof(T).Name, Ex);
}
}
}
public static XDocument Serialize<T>(T Object)
{
return Serialize<T>(Object, null);
}
public static XDocument Serialize<T>(T Object, Type[] ExtraTypes)
{
lock (Locker)
{
XDocument Xml = null;
try
{
using (MemoryStream stream = new MemoryStream())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
using (StreamReader Reader = new StreamReader(stream))
{
XmlSerializer Serializer = GetSerializer<T>(ExtraTypes);
var settings = new XmlWriterSettings { Indent = true };
using (var w = XmlWriter.Create(stream, settings))
{
Serializer.Serialize(w, Object, ns);
w.Flush();
stream.Position = 0;
}
Xml = XDocument.Load(Reader, LoadOptions.None);
foreach (XElement Ele in Xml.Root.Descendants())
{
PropertyInfo PI = typeof(T).GetProperty(Ele.Name.LocalName);
if (PI != null && PI.IsDefined(typeof(XmlCommentAttribute), false))
Xml.AddFirst(new XComment(PI.Name + ": " + PI.GetCustomAttributes(typeof(XmlCommentAttribute), false).Cast<XmlCommentAttribute>().Single().Value));
}
Reader.Close();
}
}
return Xml;
}
catch (Exception Ex)
{
throw new Exception("Could not serialize from " + typeof(T).Name + " to xml string", Ex);
}
}
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class XmlCommentAttribute : Attribute
{
public string Value { get; set; }
}
Ответ 5
Я думаю, что перемещение конструктора XMLSerializer
за пределы цикла и кэширование его результата исправит его, объяснение здесь