Memory Leak Using Streamreader and Xmlserializer

Memory Leak using StreamReader and XmlSerializer

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.

Are there still known memory leaks with XML serialization in .NET 4?

I can confirm that the XmlSerializer is still behaving the same as in older .NET versions.
A way around this leak is to implement a small caching mechanism as described here:

XmlSerializer Performance Issue when Specifying XmlRootAttribute

XmlSerializer.FromTypes producing memory leaks?

I slightly changed your code - run a loop 3000 times instead of 1000 and write serialized data into memory stream instead of file, and profile it with dotMemory. (I found using VS that RuntimeType instances are leaked, but did not find how to see where they were created).

At the end app occupied about a 1Mb of memory (I did not received a result of 500Mb as on your screenshot) but it's obviously a leak here.
Sample Image

Then opened all new objects created between the first and the fourth snapshots placed in Gen2 heap and found out that most of them are (as I mentioned above) instances of RuntimeType.
Sample Image

Opened them and saw that there are five almost equal sized groups of them.
Sample Image

So, the answer is "yes, this method produces a memory leak"

Below five stack traces where these objects were allocated

--------- 1 --------------
Allocated: 83580 B in 2985 objects

System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
System.Reflection.Emit.TypeBuilder.CreateType()
System.Xml.Serialization.XmlSerializationWriterILGen.GenerateEnd()
System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly()
Folded items
[AllThreadsRoot]

--------- 2 --------------
Allocated: 83552 B in 2984 objects

System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
System.Reflection.Emit.TypeBuilder.CreateType()
System.Xml.Serialization.XmlSerializationReaderILGen.GenerateEnd()
System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly()
Folded items
[AllThreadsRoot]

--------- 3 --------------
Allocated: 83552 B in 2984 objects

System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
System.Reflection.Emit.TypeBuilder.CreateType()
System.Xml.Serialization.XmlSerializationILGen.GenerateBaseSerializer()
System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly()
Folded items
[AllThreadsRoot]

--------- 4 --------------
Allocated: 83552 B in 2984 objects

System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
System.Reflection.Emit.TypeBuilder.CreateType()
System.Xml.Serialization.XmlSerializationILGen.GenerateTypedSerializer()
System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly()
Folded items
[AllThreadsRoot]

--------- 5 --------------
Allocated: 83552 B in 2984 objects

System.Reflection.Emit.TypeBuilder.CreateTypeNoLock()
System.Reflection.Emit.TypeBuilder.CreateType()
System.Xml.Serialization.XmlSerializationILGen.GenerateSerializerContract()
System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly()
Folded items
[AllThreadsRoot]

Is there memory Leak in Serializer

You're not disposing the "memoryStream" you declare on 6.

And something else: you won't see the memory gap regained until the GC passes... you can check that using GC.Collect

How can I eliminate a memory leak in my XMLSerializer/MemoryStream?

I posted my question in a Microsoft forum and it was correctly answered there. I invited the answerer to post their response here, but they have not done so.

The original answer in full can be found here

Paraphrasing:

The serializer creates an assembly to serialize a loaded assembly in an AppDomain which cannot be unloaded.

The workaround was to Cache the serializer by specifying the RootAttribute (only one assembly will leak, once, which is a minimum amount of memory)

This workaround is the best if all the serialized objects are the same type and the object being serialized is not too large.

In my code, I didn't have the RootAttribute specified in the XMLSerializer constructor which was causing a new assembly to be created each time I called the serializer.

Changing from this

''' <summary>
''' Static constructor that initialises the serializer for this type
''' </summary>
Shared Sub New()
serializer = New XmlSerializer(GetType(T))
End Sub

To this:

''' <summary>
''' Static constructor that initialises the serializer for this type
''' </summary>
Shared Sub New()
serializer = New XmlSerializer(GetType(T), XmlRootAttribute(GetType(T).ToString))
End Sub

Completely resolved the leaking.

For a more in depth explanation for why this worked, please see the original answer in the link I posted above.

Please note that I later had to modify the constructor a bit for the results to make sense, but the above was the minimum required to fix the leak.

Shared Sub New()
Dim root As String = GetType(T).ToString
root = root.Substring(root.LastIndexOf(".") + 1, root.Length - root.LastIndexOf(".") - 1)
Dim rootNode As New XmlRootAttribute(root)
rootNode.Namespace = "<appropriate.xsd>"
serializer = New XmlSerializer(GetType(T), rootNode)
End Sub


Related Topics



Leave a reply



Submit