Original post at: https://siderite.dev/blog/xmlserializer-memory-leak/
So I was happily minding my own business after a production release only for everything to go BOOM! Apparently, maybe because of something we did, but maybe not, the memory of the production servers was running out. Exception looked something like:
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown. at System.Reflection.Emit.TypeBuilder.SetMethodIL(RuntimeModulemodule, Int32tk ,BooleanisInitLocals, Byte body, Int32 bodyLength, Byte LocalSig ,Int32sigLength, Int32maxStackSize, ExceptionHandler exceptions, Int32numExceptions ,Int32 tokenFixups, Int32numTokenFixups) at System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() at System.Reflection.Emit.TypeBuilder.CreateType() at System.Xml.Serialization.XmlSerializationReaderILGen.GenerateEnd(String methods, XmlMapping xmlMappings, Type types) at System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly(XmlMapping xmlMappings, Type types, StringdefaultNamespace ,Evidenceevidence) at System.Xml.Serialization.TempAssembly..ctor(XmlMapping xmlMappings, Type types, StringdefaultNamespace ,Stringlocation, Evidenceevidence) at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMappingxmlMapping, Typetype ,StringdefaultNamespace, Stringlocation, Evidence evidence) at System.Xml.Serialization.XmlSerializer..ctor(Typetype, XmlAttributeOverrides overrides, Type extraTypes, XmlRootAttributeroot, StringdefaultNamespace, Stringlocation, Evidence evidence) at System.Xml.Serialization.XmlSerializer..ctor(Typetype, XmlAttributeOverrides overrides)
At first I thought there was something else eating away the memory, but the exception was repeatedly thrown at this specific point. And I did what every senior dev does: googled it! And I found this answer: "When an XmlSerializer is created, an assembly is dynamically generated and loaded into the AppDomain. These assemblies cannot be garbage collected until their AppDomain is unloaded, which in your case is never." It also referenced a Microsoft KB886385 from 2007 which, of course, didn't exist at that URL anymore, but I found it archived by some nice people.
What was going on? I would tell you, but Gergely Kalapos explains things much better in his article How the evil System.Xml.Serialization.XmlSerializer class can bring down a server with 32Gb ram. He also explains what commands he used to debug the issue, which is great!
But since we already know links tend to vanish over time (so much for stuff on the Internet living forever), here is the gist of it all:
- XmlSerializer generates dynamic code (as dynamic assemblies) in its constructors
- the most used constructors of the class have a caching mechanism in place:
- XmlSerializer.XmlSerializer(Type, String)
- but the others do not, so every time you use one of those you create, load and never unload another dynamic assembly
I know this is an old class in an old framework, but some of us still work in companies that are firmly rooted in the middle ages. Also since I plan to maintain my blog online until I die, it will live on the Internet for the duration.
Hope it helps!