";类型';的异常;System.OutOfMemoryException';被抛出”;同时使用xm
本文关键字:xm OutOfMemoryException 类型 quot 异常 System | 更新日期: 2023-09-27 18:21:16
我正在使用以下代码来获取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;
}
我有一个使用该方法运行的循环,过了一段时间后,我得到了一个内存不足的异常,如下所示:
异常的原因可能是什么?using语句真的在处理流吗?或者我可以使用其他什么替代方案?
我认为这里的问题是组装饱和。CCD_ 1通过动态生成程序集来工作;如果使用XmlSerializer(Type)
构造函数,它会缓存它并查找它;但对于任何其他构造函数,它都不是。并且程序集不能(通常)卸载。所以你只会有越来越多的集会吞噬你的记忆。如果你在循环中运行这个,你将需要缓存序列化程序:
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中发生了变化,在这种情况下我可能不知道)。这意味着,如果你有很多字符串被分配并保留,那么最终会有越来越少的连续可用空间块,这些块足够大,可以分配下一个字符串。这是因为当其他内存块被释放时,没有进程将分配的块压缩在一起。如上所述,在执行此类操作时,这很容易导致内存不足异常。
本文很好地概述了大型对象堆的"危险"和注意事项。
您对这个方法返回的字符串做了什么?生成的字符串有多大?