2015-07-16 2 views
2

Я получаю OutOfMemoryException при вызове ToString на моем StringWriter:OutOfMemoryException в StringWriter.ToString()

StringWriter stringWriter = new System.IO.StringWriter(); 
XmlSerializer serializer = new XmlSerializer(typeof(T)); 
serializer.Serialize(stringWriter, data); 
string xmlString = stringWriter.ToString(); // <-- Exception occurs here 

Как я могу решить эту проблему?

+6

У меня есть подозрение, вы можете иметь [циклическую ссылку] (http://stackoverflow.com/a/5004495/314291) в ваш сериализованный график. Возможно, вам придется переключиться на DataContractSerializer. – StuartLC

+0

Размер объекта огромный .. как его оптимизировать – vmb

+3

Если есть круговая ссылка, более вероятно, что будет выведено исключение StackOverflowException. –

ответ

0

Он работает, делая так ..

using (MemoryStream ms = new MemoryStream()) 
      { 
       serializer.Serialize(ms, data); 
       ms.Position = 0; 
       XmlReader reader = XmlReader.Create(ms, schemaReaderSettings); 
      } 
0

Уверен, что stringWriter уже очень большой, допустим, размер более 1 Гб и что ваш компьютер имеет общую память 2 ГБ. Когда вы вызываете ToString(), память будет удваиваться, потому что новая строка будет создана и распределена в куче размером 1 ГБ, чтобы иметь возможность скопировать содержимое stringWriter. Попытайтесь избежать ненужного ToString() и попытайтесь сделать то, что вам нужно, используя напрямую stringWriter. Избегая ToString(), вы обычно уменьшаете объем памяти на 50%.

Но если вам действительно нужны ваши данные в виде строки, и у вас недостаточно памяти, попробуйте сначала сохранить содержимое в файле, удалите StringWriter и загрузите содержимое файла в строку с помощью StreamReader.ReadToEnd() API.

Еще один пример - попытаться разрезать нечеткие сериализованные данные и попытаться разобрать их в кусках. Например, давайте рассмотрим XML, который имеет структуру, подобную этой:

<Root> 
    <Item>some data 1</Item> 
    <Item>some data 2</Item> 
    <Item>some data 3</Item> 
    .... 
    <Item>some data n</Item> 
</Root> 

Вы можете сериализовать объект в MemoryStream, а затем прочитать его на куски и создавать «маленькие» XML-данные из ваших куски, которые выглядят так:

Первый XML:

<Root> 
    <Item> some data 1 </Item> 
</Root> 

Второй XML:

<Root> 
    <Item> some data 2 </Item> 
</Root> 

И и так далее, которые могут быть проверены индивидуально и проверены.

+0

Я делаю это bcoz, после этого выполнения, как XmlReader reader = XmlReader.Create (новый StringReader (xmlString), schemaReaderSettings); – vmb

+0

@vmb Есть ли причина для сериализации ваших данных в XML, а затем сразу же чтение XML? Не можете ли вы просто использовать свои данные? – cubrr

+0

да .. есть причина .. для проверки – vmb

0

это следует сделать трюк:

using (StreamWriter sw = File.AppendText(path)) 
{ 
    using (StreamReader sr = new StreamReader(ms)) 
    { 
     while (sr.Peek() >= 0) 
     { 
      string l = sr.ReadLine(); 

      sw.WriteLine(l); 
     } 
    } 
} 
+0

Я пробовал это ... .. Исключить исключение OutofMemory в строке xmlString = sr.ReadToEnd(); – vmb

+0

OutOfMemory означает, что говорит.У вас заканчивается память :) Когда у вас недостаточно памяти, вы должны больше использовать свой жесткий диск: D это то, что Windows делает с виртуальной памятью тоже. –

+0

sr.ReadLine() работает лучше :) – Chaka

1

Попробуйте этот код. Он использует файл как временный буфер.

List<Dummy> lst = new List<Dummy>(); 

     for (var i = 0; i < 100000; i++) 

     { 
      lst.Add(new Dummy() 
        { 
         X = i, 
         Y = i * 2 
        }); 

     } 

     XmlSerializer serializer = new XmlSerializer(typeof(List<Dummy>)); 

     // estimate your memory consumption ... it would be around 4 bytes reference + 4 bytes object type pointer + 8 bytes those ints + let's say another 4 bytes other hidden CLR metadatas. a total of 20 bytes per instance + 4 bytes reference to our object (in the list array) => around 24 bytes per instance. Round up to a let's say 50 bytes per instance. Multiply it by 100.000 = 5.000.000 

     MemoryStream memStream = new MemoryStream(5000000); 

     serializer.Serialize(memStream, lst); 
     memStream.Position = 0; 

     string tempDatafileName = null; 
     var dataWasWritten = false; 
     try 
     { 
      var fileName = Guid.NewGuid().ToString() + ".tempd"; 
      var specialFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); 

      using (var fs = new FileStream(tempDatafileName, FileMode.Create, FileAccess.ReadWrite)) 
       memStream.WriteTo(fs); 

      dataWasWritten = true; 

      memStream.Dispose(); 
      memStream = null; 

      lst.Clear(); 
      lst = null; 
      // force a full second generational GC 
      GC.Collect(2); 

      // reading the content in string 
      string myXml = File.ReadAllText(tempDatafileName); 
     } 
     finally 
     { 
      if (dataWasWritten && string.IsNullOrWhiteSpace(tempDatafileName) == false) 
      { 
       if (File.Exists(tempDatafileName)) 
       { 
        try 
        { 
         File.Delete(tempDatafileName); 
        } 
        catch 
        { 

        } 
       } 
      } 
     } 
+0

Как вы можете видеть, поскольку операция записи и операция чтения из временного файла находятся в одном стеке вызовов, я делаю этот трюк memStream = null; так что сборщик мусора будет удалять мой объект, прежде чем очень большие данные вернутся из временного файла. Если я этого не сделаю, GC увидит, что есть ссылка на наш поток памяти (ссылка, которая находится в стеке) –

+0

Я не пробовал это ... но я думаю, что это лучшее решение – vmb