内存不足异常读取“大”文件

时间:2014-12-16 12:54:45

标签: c# serialization out-of-memory

我正在尝试将对象序列化为字符串 我遇到的第一个问题是XMLSerializer.Serialize方法抛出了内存不足异常,我尝试了所有类型的解决方案而没有工作,所以我将它序列化为一个文件。 该文件大约300mb(32位进程,8gb ram)并尝试使用StreamReader.ReadToEnd读取它也会导致内存不足异常。

XML格式并将其加载到字符串上不是必须的选项。 问题是:

  • 300mb文件会抛出这种异常的原因是什么? 300mb并不是一个大文件。

.Serialize

上失败的序列化代码
using (MemoryStream ms = new MemoryStream())
{
    var type = obj.GetType();
    if (!serializers.ContainsKey(type))
        serializers.Add(type,new XmlSerializer(type));

   // new XmlSerializer(obj.GetType()).Serialize(ms, obj);
    serializers[type].Serialize(ms, obj);
    ms.Position = 0;

    using (StreamReader sr = new StreamReader(ms))
    {
        return sr.ReadToEnd();
    }                  
}

序列化并从ReadToEnd上失败的文件中读取     var type = obj.GetType();     if(!serializers.ContainsKey(type))     serializers.Add(type,new XmlSerializer(type));

FileStream fs = new FileStream(@"c:/temp.xml", FileMode.Create);
TextWriter writer = new StreamWriter(fs, new UTF8Encoding());
serializers[type].Serialize(writer, obj);
writer.Close();
fs.Close();
using (StreamReader sr = new StreamReader(@"c:/temp.xml"))
{
   return sr.ReadToEnd();
}

对象很大,因为它是一个精心设计的系统整个配置对象......

更新: 在chucks中读取文件(8 * 1024个字符)会将文件加载到StringBuilder中,但构建器在ToString()上失败....开始认为没有办法真的很奇怪。

2 个答案:

答案 0 :(得分:4)

是的,如果您使用32位,尝试在一个块中加载300MB将会笨拙,尤其是在使用不知道最终大小的方法时(字符数)而不是字节),因此必须保持内部缓冲区加倍。这就是处理字符串时!然后需要将其转换为DOM,这通常需要几倍于底层数据的空间。最后,你需要将它反序列化为实际的对象,通常再次采用相同的方法。

所以 - 确实,尝试以32位方式执行此操作将非常困难。

首先要尝试的是:不要使用ReadToEnd - 只需将XmlReader.Create与文件路径或FileStream一起使用,让XmlReader担心如何使用{{1}}加载数据。不要为它加载内容。

之后......接下来要做的是:不要将其限制为32位。

好吧,你可以尝试启用3GB开关,但是......最好转移到64位。

除此之外:xml 是大量数据的理想选择。

答案 1 :(得分:1)

探索StreamReader.ReadToEnd的源代码,发现它在内部使用了StringBuilder.Append方法:

public override String ReadToEnd()
{
    if (stream == null)
        __Error.ReaderClosed();

#if FEATURE_ASYNC_IO
    CheckAsyncTaskInProgress();
#endif

    // Call ReadBuffer, then pull data out of charBuffer.
    StringBuilder sb = new StringBuilder(charLen - charPos);
    do {
        sb.Append(charBuffer, charPos, charLen - charPos);
        charPos = charLen;  // Note we consumed these characters
        ReadBuffer();
    } while (charLen > 0);
    return sb.ToString();
}

最有可能抛出此异常导致此问题/答案:interesting OutOfMemoryException with StringBuilder