Memory Leak using StreamReader and XmlSerializer

First off, you should be disposing of your StreamReader even if an exception is thrown (same for XMLObj). Use the using statement. Currently you will not dispose when an exception is thrown.

It is very unlikely that you have a memory leak. More likely, the runtime simply did not elect to collect memory yet. Even GC.Collect will not necessarily cause memory to be released.

I have run into similar situations when processing very large XML files (multi-GB). Even though the runtime grabs most available memory, it does release it when memory pressure warrants.

You can use the memory profiler in Visual Studio to see what memory is allocated, and in what generation it resides.

UPDATE

The comment from @KaiEichinger is worth investigating. It indicates that the XmlSerializer may be creating a new cached object definition for every loop iteration

XMLSerializer constructor creates the temporary assembly for the type to be serialized using reflection and since code generation is expensive the assembly is cached in the memory on per type basis. But many times root name will be changed and can be dynamic and it will not cache the dynamic assembly. So whenever the above line of code is called it loads the new assembly every time and will stay in the memory until AppDomain is unloaded.


The leak is here:

new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"))

XmlSerializer uses assembly generation, and assemblies cannot be collected. It does some automatic cache/reuse for the simplest constructor scenarios (new XmlSerializer(Type), etc), but not for this scenario. Consequently, you should cache it manually:

static readonly XmlSerializer mySerializer =
    new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"))

and use the cached serializer instance.


From MSDN:enter link description here

To increase performance, the XML serialization infrastructure dynamically generates assemblies to serialize and deserialize specified types. The infrastructure finds and reuses those assemblies. This behavior occurs only when using the following constructors:

XmlSerializer.XmlSerializer(Type)

XmlSerializer.XmlSerializer(Type, String)

If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.

=> So to fix it you have to use this constructor XmlSerializer xml = new XmlSerializer(typeof(XMLObj)) instead of XmlSerializer xml = new XmlSerializer(typeof(XMLObj), new XmlRootAttribute("rootNode"));

and add root XML attribute into XMLObj class.

[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);
    }
}