“使用xmlserializer时抛出了’System.OutOfMemoryException’类型的exception

我使用以下代码来获取xml字符串。

public static string ToXMLString(object obj, string nodeName) { XmlSerializer xmlSerializer = default(XmlSerializer); string xml = string.Empty; StreamReader r = default(StreamReader); try { if (obj != null) { using (MemoryStream m = new MemoryStream()) { using (XmlWriter writer = XmlWriter.Create(m, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true })) { // Don't include XML namespace XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces(); xmlnsEmpty.Add("", ""); if (xmlSerializer == null) xmlSerializer = new XmlSerializer(obj.GetType(), new XmlRootAttribute(nodeName)); xmlSerializer.Serialize(writer, obj, xmlnsEmpty); m.Flush(); m.Position = 0; r = new StreamReader(m); xml = r.ReadToEnd(); xmlSerializer = null; } } } return xml; } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } finally { r.Close(); r.Dispose(); } //XmlSerializer xmlSerializer; } 

我有一个使用该方法运行的循环,一段时间后我得到一个内存不足exception,如下所示:

什么可能是例外的原因? 使用语句真的处理流吗? 或者我可以使用其他替代品?

我希望这里的问题是assembly饱和。 XmlSerializer通过动态生成程序集来工作; 如果你使用XmlSerializer(Type)构造函数,它会缓存它并查找它; 但对于任何其他构造函数,它没有。 并且assembly不能(通常)卸载。 所以你只是得到越来越多的集会记忆。 如果您在循环中运行它,则需要缓存序列化程序:

 using System; using System.Collections; using System.IO; using System.Xml; using System.Xml.Serialization; public static class Program { static void Main() { // the loop here is from your comment for (int i = 0; i < 10000000; i++) { ToXMLString("test", string.Format("test")); Console.WriteLine(i); } } // why is this Hashtable? due to the threading semantics! private static readonly Hashtable serializerCache = new Hashtable(); public static string ToXMLString(object obj, string nodeName) { if (obj == null) throw new ArgumentNullException("obj"); Type type = obj.GetType(); var cacheKey = new { Type = type, Name = nodeName }; XmlSerializer xmlSerializer = (XmlSerializer)serializerCache[cacheKey]; if (xmlSerializer == null) { lock (serializerCache) { // double-checked xmlSerializer = (XmlSerializer)serializerCache[cacheKey]; if (xmlSerializer == null) { xmlSerializer = new XmlSerializer(type, new XmlRootAttribute(nodeName)); serializerCache.Add(cacheKey, xmlSerializer); } } } try { StringWriter sw = new StringWriter(); using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings() { OmitXmlDeclaration = true, Indent = true })) { // Don't include XML namespace XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces(); xmlnsEmpty.Add("", ""); xmlSerializer.Serialize(writer, obj, xmlnsEmpty); } return sw.ToString(); } catch (Exception ex) { Console.Error.WriteLine(ex.Message); throw; } } } 

这里的问题可能不是这个代码本身,而是你在这个方法之外用生成的字符串做什么。

根据您的序列化,可能会产生许多大字符串。 如果你绕着你的循环抓住这些字符串,你就会消耗越来越多的内存。 更糟糕的是,即使使用的绝对内存量可能不是很大,但很可能这些大字符串导致内存碎片 – GC可能无法为下一个字符串分配连续的内存块。

在CLR中 – 大型对象(我觉得大到85KB左右)没有分配给通常的GC代; 相反,他们进入大对象堆。 这个堆永远不会被压缩(除非在.Net 4中已经改变了,在这种情况下我可能不知道它)。 这意味着如果你有很多字符串被分配并被保留,那么最终会有越来越少的连续可用空间块,这些空间足够大以分配你的下一个字符串这是因为没有进程来压缩分配的当释放其他内存块时,它们会一起阻塞。 如上所述,当完成这种操作时,这很容易引起内存不足exception。

本文很好地概述了大对象堆的“危险性”和注意事项。

你对这个方法返回的字符串做了什么,以及生成的字符串有多大?