3

I am getting an OutOfMemoryException when calling ToString on my StringWriter:

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

How can I solve this issue?

cbr
  • 12,563
  • 3
  • 38
  • 63
vmb
  • 2,878
  • 15
  • 60
  • 90

4 Answers4

1

Try this code. It uses a file as a temporary buffer.

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
                    {

                    }
                }
            }
        }
George Lica
  • 1,798
  • 1
  • 12
  • 23
  • As you can see, because write operation and read operation from temp file are on the same call stack, I am doing this trick memStream = null; so that garbage collector will dispose my object before a very large data will come back from the temp file. If I don't do this, GC will see that there is a reference to our memory stream (a reference that resides in stack) – George Lica Jul 16 '15 at 07:58
  • i didnt try this..but i think it is a better solution – vmb Jul 23 '15 at 03:43
0

I am sure that stringWriter is already very large, let's say a size more than 1 Gb and that your computer has a total memory of 2 GB. When you call ToString () the memory will get doubled because a new string will be created and allocated in heap of size 1GB to be able to copy the content of stringWriter. Try to avoid the unnecesary ToString () and try to do what you need to do using directly the stringWriter. Avoiding ToString(), normally you would decrease memory footprint by 50%.

But if you really need your data as string and you don't have sufficient memory, try to save the content first in a file, dispose the StringWriter and load the file content into a string using a StreamReader.ReadToEnd () API.

Another aproach would be to try to cut un chunks your serialized data and try to parse it in chunks. For example, let's consider an XML that has a structure like this:

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

You could serialize your object into a MemoryStream, then read it in chunks and create "little" XML data from your chunks that looks like this:

First xml:

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

Second xml:

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

And so on, that could be checked individually and validated.

George Lica
  • 1,798
  • 1
  • 12
  • 23
  • i am doing this bcoz ,after this opertion doing like XmlReader reader = XmlReader.Create(new StringReader(xmlString), schemaReaderSettings); – vmb Jul 16 '15 at 06:42
  • @vmb Is there a reason for serializing your data to XML, then immediately reading the XML? Can't you just directly use your `data`? – cbr Jul 16 '15 at 06:43
0

this should do the trick :

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

            sw.WriteLine(l);
        }
    }
}
Chaka
  • 377
  • 1
  • 9
  • i tried this one..same OutofMemory exception in line xmlString = sr.ReadToEnd(); – vmb Jul 16 '15 at 06:51
  • OutOfMemory means what is says. You are running out of memory :) When you don't have enough memory you must start using your hard drive more :D that's what Windows is doing with virtual memory too. – George Lica Jul 16 '15 at 07:22
  • 1
    @GeorgeLica - If you are running out of 2GB of memory the chances are you are doing something wrong. So adding the hard drive is just going to allow you do use more storage while doing something wrong. It's better to fix what is wrong. – Enigmativity Jul 16 '15 at 08:24
  • @Enigmativity Yep in 99.99% of cases you are right but it might be the case that you really need (at least once) to process very large objects having a "normal" hardware configuration. – George Lica Jul 16 '15 at 10:27
0

It is working when doing like this..

   using (MemoryStream ms = new MemoryStream())
            {
                serializer.Serialize(ms, data);
                ms.Position = 0;
                XmlReader reader = XmlReader.Create(ms, schemaReaderSettings);
            }
vmb
  • 2,878
  • 15
  • 60
  • 90