Подтвердить что ты не робот

Утечка памяти с использованием 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(); но это ничего не делает.

4b9b3361

Ответ 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 за пределы цикла и кэширование его результата исправит его, объяснение здесь