如何对包含大量对象的对象进行序列化?

6

我正在使用c#和.net 4.5。

我有一个名为SomeData的类,其中包含一个成员_SomeEvents,它是一个字典。 SomeData类还包含一堆信息,例如生成数据的时间,生成数据的用户等。

我试图使用formatter.Serialize将SomeData对象保存到文件中,但在对象很大(例如1GB)时遇到OutOfMemoryException。

IFormatter formatter = new BinaryFormatter();
Stream stream;
stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, _myObject);
stream.Close();

我读过其他文章,他们认为内存不足的问题是由于无法找到足够大的连续空闲内存区域来生成要写入磁盘的数据。我也读到说这是“错误”的序列化方式--我的假设是,如果我正确地处理序列化,CLR将在写入数据时逐步完成,而不是在保存之前尝试准备所有数据。话虽如此 - 在序列化操作失败之前,我看到一个大文件被创建,这意味着它正在按顺序写入。

我尝试将Serialize操作更改为直接写入Dictionary对象本身,而不是包含Dictionary的对象 - 但遇到了相同的问题,我收到了内存异常。

问题:

  1. 为什么Serialize会遇到这个内存问题 - 即使我给它要序列化的Dictionary对象 - 因为它肯定是逐步写入数据?
  2. 有没有更好的方法?!!

以下是完整的异常信息:

无法保存文件:System.OutOfMemoryException: 引发了类型为 'System.OutOfMemoryException' 的异常。

at System.Runtime.Serialization.ObjectIDGenerator.Rehash()

at System.Runtime.Serialization.ObjectIDGenerator.GetId(Object obj, Boolean& firstTime)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.InternalGetId(Object obj, Boolean assignUniqueIdToValueType, Type type, Boolean& isNew)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteString(NameInfo memberNameInfo, NameInfo typeNameInfo, Object stringObject)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteKnownValueClass(NameInfo memberNameInfo, NameInfo typeNameInfo, Object data)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteMembers(NameInfo memberNameInfo, NameInfo memberTypeNameInfo, Object memberData, WriteObjectInfo objectInfo, NameInfo typeNameInfo, WriteObjectInfo memberObjectInfo)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteMemberSetup(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, String memberName, Type memberType, Object memberData, WriteObjectInfo memberObjectInfo)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, String[] memberNames, Type[] memberTypes, Object[] memberData, WriteObjectInfo[] memberObjectInfos)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)

at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)

at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)

at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)


请参见以下帖子:https://social.msdn.microsoft.com/forums/vstudio/en-US/4aa0ebcc-1585-403f-a249-9ae435812f21/max-size-xml-serialize-can-handle - jdweng
谢谢,虽然看起来 https://dev59.com/PYLba4cB1Zd3GeqPbTVz 在正确的领域内。我要尝试逐个处理我的每个事件的序列化操作1(或x)次,看看是否可以避免使用BinaryFormatter图形序列化器时出现的问题。 - gf131072
3
我刚刚转换到protobuf-net,我的内存困扰问题已经消失了!尽管对于1m个事件,最终我会得到一个400MB的文件,但是我可以将其写入临时文件,然后在进行初始反序列化之后将其压缩为8MB。 - gf131072
请参考此帖子 https://dev59.com/r1DTa4cB1Zd3GeqPNe4J。 - Rafael Semann
1个回答

0
使用流来序列化每个对象。看一个例子: 注意:这只是一个模板,不是完整的代码。我只是想给你一个思路。
private void SerializeObjects(List<foo> foos, Stream stream)
{
    foreach (var f in foos)
    {
        stream.Write(f);
    }
}

private void DeserializeObjects(List<foo> foos, Stream stream)
{
    foo f = stream.ReadFoo();
    while (f != null)
    {
        foos.Add(f);
        f = stream.ReadFoo();
    }
}

我已经成功地使用SerializeObjects,但是stream.ReadFoo()是什么?这是我必须自己编写的方法吗? - Matthew
ReadFoo 只是一个示例,因为您可以从许多方式读取。只需使用流中所需的读取即可。请参见流类。我会说 MemoryStream 是您必须进行序列化和反序列化的东西。 - Marc Roussel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接