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.
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.
Opened them and saw that there are five almost equal sized groups of them.
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
Calling a Function from a String in C#
What Are the Differences Between Generics in C# and Java... and Templates in C++
How to Parse a Json String That Would Cause Illegal C# Identifiers
Passing Strings from C# to C++ Dll and Back - Minimal Example
Is Is Possible to Export Functions from a C# Dll Like in VS C++
How to Call a C# Library from Native C++ (Using C++\Cli and Ijw)
Reach Control from Another Page. Asp.Net
How to Dynamically Generate HTML Code Using .Net'S Webbrowser or Mshtml.Htmldocument
Convert Webpage to Image from Asp.Net
How to Find the Text Within a Div in the Source of a Web Page Using C#
How to Convert HTML to Plain Text
What Is the Yield Keyword Used For in C#
Easiest Way to Read from and Write to Files