";类型';的异常;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语句真的在处理流吗?或者我可以使用其他什么替代方案?

";类型';的异常;System.OutOfMemoryException';被抛出”;同时使用xm

我认为这里的问题是组装饱和。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中发生了变化,在这种情况下我可能不知道)。这意味着,如果你有很多字符串被分配并保留,那么最终会有越来越少的连续可用空间块,这些块足够大,可以分配下一个字符串。这是因为当其他内存块被释放时,没有进程将分配的块压缩在一起。如上所述,在执行此类操作时,这很容易导致内存不足异常。

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

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