2012-01-25 4 views
3

Я использую следующий код для получения строки xml.«Исключение типа» System.OutOfMemoryException «было выбрано» при использовании xmlserializer

public static string ToXMLString(object obj, string nodeName) 
{ 
    XmlSerializer xmlSerializer = default(XmlSerializer); 
    string xml = string.Empty; 
    StreamReader r = default(StreamReader); 
    try 
    { 
     if (obj != null) 
     { 
      using (MemoryStream m = new MemoryStream()) 
      { 
       using (XmlWriter writer = XmlWriter.Create(m, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true })) 
       { 
        // Don't include XML namespace 
        XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces(); 
        xmlnsEmpty.Add("", ""); 
        if (xmlSerializer == null) 
         xmlSerializer = new XmlSerializer(obj.GetType(), new XmlRootAttribute(nodeName)); 
        xmlSerializer.Serialize(writer, obj, xmlnsEmpty); 

        m.Flush(); 
        m.Position = 0; 

        r = new StreamReader(m); 
        xml = r.ReadToEnd(); 
        xmlSerializer = null; 
       } 
      } 
     } 

     return xml; 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 
     throw; 
    } 
    finally 
    { 
     r.Close(); 
     r.Dispose(); 
    } 
    //XmlSerializer xmlSerializer; 

} 

У меня есть цикл, который выполняется с использованием метода и через некоторое время я получить из исключения памяти, как показано ниже:

Что может причину исключения? Является ли использование инструкции действительно распоряжаться потоками? Или какую другую альтернативу я могу использовать?

+0

это не повлияет на код, но нет никакой пользы объявить эти переменные в верхней, ни назначение 'xmlSerializer' в' null' в конце; Я просто объявляю строку, т. Е. «Var xmlSerializer = новый XmlSerializer (...);». Вы не избавляете читателя, но это не будет убивать вас здесь. На самом деле, вы можете написать «StringBuilder», если хотите просто строку - избегать возиться с «потоком» ... –

+0

Можем ли мы увидеть модель объекта, которую вы сериализуете? Я * подозреваю * это связано с моделью - что-то вроде циклической ссылки (то есть уклонение от встроенного обнаружения - не невозможно) или что-то подобное –

+0

'using' вызывает' IDisposable.Dispose() 'при выходе из блока использования , Так что часть вашего кода в порядке. –

ответ

9

Я ожидал бы, что проблема здесь сборка насыщенность. XmlSerializer работает, создавая сборку на лету; если вы используете конструктор XmlSerializer(Type), он кэширует его и ищет его; но для любого другого конструктора это не так. А сборки не могут (обычно) разгружаться. Таким образом, вы просто получаете все больше и больше ассамблей, которые едят вашу память. Вам нужно будет кэшировать сериалайзер, если вы работаете это в цикле:

using System; 
using System.Collections; 
using System.IO; 
using System.Xml; 
using System.Xml.Serialization; 


public static class Program 
{ 
    static void Main() 
    { 
     // the loop here is from your comment 
     for (int i = 0; i < 10000000; i++) { ToXMLString("test", string.Format("test")); Console.WriteLine(i); } 
    } 

    // why is this Hashtable? due to the threading semantics! 
    private static readonly Hashtable serializerCache = new Hashtable(); 

    public static string ToXMLString(object obj, string nodeName) 
    { 
     if (obj == null) throw new ArgumentNullException("obj"); 
     Type type = obj.GetType(); 
     var cacheKey = new { Type = type, Name = nodeName }; 
     XmlSerializer xmlSerializer = (XmlSerializer)serializerCache[cacheKey]; 
     if (xmlSerializer == null) 
     { 
      lock (serializerCache) 
      { // double-checked 
       xmlSerializer = (XmlSerializer)serializerCache[cacheKey]; 
       if (xmlSerializer == null) 
       { 
        xmlSerializer = new XmlSerializer(type, new XmlRootAttribute(nodeName)); 
        serializerCache.Add(cacheKey, xmlSerializer); 
       } 
      } 
     } 
     try 
     { 

      StringWriter sw = new StringWriter(); 
      using (XmlWriter writer = XmlWriter.Create(sw, 
       new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true })) 
      { 
       // Don't include XML namespace 
       XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces(); 
       xmlnsEmpty.Add("", ""); 
       xmlSerializer.Serialize(writer, obj, xmlnsEmpty); 
      } 
      return sw.ToString(); 
     } 
     catch (Exception ex) 
     { 
      Console.Error.WriteLine(ex.Message); 
      throw; 
     } 
    } 
} 
+0

для (int i = 0; i <10000000; i ++) { t.ToXMLString ("test", string.Format ("test")); Console.WriteLine (i); GC.Collect(); } – user1010863

+0

@ user1010863 Я понятия не имею, как интерпретировать этот комментарий ... - также, 'string.Format (" test ")' - just .... why? –

+0

Thats, как я использую метод из моего приложения. Я все еще смущен тем, что могло бы сделать большой объем памяти. Его цикл, который запускает вызов функции. Глядя на диспетчера задач, физическая память для devenv продолжает увеличиваться примерно до 83%, где я получаю исключение. – user1010863

5

Проблема здесь не в этом коде, а в том, что вы делаете с генерируемой строкой вне этого метода.

В зависимости от того, что вы сериализуете, вероятно, это приводит к появлению многих больших строк. Если вы держитесь за эти строки, когда идете по кругу, вы, вероятно, будете потреблять больший и больший объем памяти. Чтобы ухудшить ситуацию, несмотря на то, что абсолютный размер используемой памяти не может быть огромным, весьма вероятно, что эти большие строки вызывают фрагментацию памяти - GC может не иметь возможности выделить непрерывный блок памяти для следующего строка.

В CLR - большие объекты (большие, чем-то около 85 КБ, я думаю) не выделяются обычным поколениям GC; вместо этого они идут в кучу больших объектов. Эта куча никогда не уплотняется (если это не изменилось в .Net 4, в этом случае я, возможно, не знаю). Следствием этого является то, что если у вас есть много строк, выделенных и удерживаемых, то получается меньше и меньше блоков смежного свободного пространства, которые достаточно велики, чтобы выделить следующую строку. Это связано с тем, что нет никакого процесса для уплотнения выделенных блокируется вместе, когда освобождаются другие блоки памяти. Это, довольно легко, может привести к исключениям из-за-памяти, как описано выше, при выполнении такого рода операций.

This статья дает хороший обзор «опасностей» и соображений большой кучи объектов.

Что вы делаете со строками, возвращаемыми этим методом, и насколько велики генерируемые строки?

+0

Из того, что я прочитал, куча больших объектов теперь уплотняется с .Net 4.5 (и выше) – musefan

Смежные вопросы