使用 DataContractSerializer 序列化对象后调用 MemoryStream.ToArray() 时出现 System.OutOfMemory 异常
我在这个声明中遇到间歇性的“内存不足”异常:
return ms.ToArray();
在此方法中:
public static byte[] Serialize(Object inst)
{
Type t = inst.GetType();
DataContractSerializer dcs = new DataContractSerializer(t);
MemoryStream ms = new MemoryStream();
dcs.WriteObject(ms, inst);
return ms.ToArray();
}
我怎样才能防止它?有更好的方法吗?
ms 的长度是 182,870,206 字节(174.4 MB),
我将其放入字节数组中,以便我可以通过压缩运行它并将其存储到磁盘。这些数据(显然)是一个自定义类的大列表,当我的 silverlight 应用程序启动时,我正在从 WCF 服务器下载该类。我将其序列化并压缩,因此它在独立存储中仅使用大约 6MB。下次用户从网络访问并运行 silverlight 应用程序时,我会检查时间戳,如果好的话,我只需从隔离中打开文件,解压缩它,反序列化它,然后加载我的结构。我将整个结构保留在内存中,因为应用程序主要用于操作该结构的内容。
@配置器是正确的。数组的大小太大。我通过自己的序列化器进行滚动,声明一个 [列表记录数 * 每条记录的字节数] 的字节数组,然后自己使用这样的语句直接填充它:
Buffer.BlockCopy(
BitConverter.GetBytes(node.myInt),0,destinationArray,offset,sizeof(int));
offset += sizeof(int);
然后将其取回:
newNode.myInt= BitConverter.ToInt32(sourceByteArray,offset);
offset += sizeof(int);
然后我压缩它并存储它到隔离存储。
我的大小从使用 DataContractSerializer 时的 174MB 变为使用我的时的 14MB。 压缩后,独立存储中的文件大小从 6MB 变为 1MB。
感谢 Configurator 和 Filip 的帮助。
I am getting an intermittent "out of memory" exception at this statement:
return ms.ToArray();
In this method:
public static byte[] Serialize(Object inst)
{
Type t = inst.GetType();
DataContractSerializer dcs = new DataContractSerializer(t);
MemoryStream ms = new MemoryStream();
dcs.WriteObject(ms, inst);
return ms.ToArray();
}
How can I prevent it? Is there a better way to do this?
The length of ms is 182,870,206 bytes (174.4 MB)
I am putting this into a byte array so that I can then run it through compression and store it to disk. The data is (obviously) a large list of a custom class that I am downloading from a WCF server when my silverlight application starts. I am serializing it and compressing it so it uses only about 6MB in isolated storage. The next time the user visits and runs the silverlight application from the web, I check the timestamp, and if good I just open the file from isolated, decompress it, deserialize it, and load my structure. I am keeping the entire structure in memory because the application is mostly geared around manipulating the contents of this structure.
@configurator is correct. The size of the array was too big. I rolled by own serializer, by declaring a byte array of [list record count * byte count per record], then stuffed it directly myself using statements like this to stuff it:
Buffer.BlockCopy(
BitConverter.GetBytes(node.myInt),0,destinationArray,offset,sizeof(int));
offset += sizeof(int);
and this to get it back:
newNode.myInt= BitConverter.ToInt32(sourceByteArray,offset);
offset += sizeof(int);
Then I compressed it and stored it to isolated storage.
My size went from 174MB with the DataContractSerializer to 14MB with mine.
After compression it went from a 6MB to a 1MB file in isolated storage.
Thanks to Configurator and Filip for their help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我不知道你如何使用该代码,但令我印象深刻的一件事是你没有释放你的资源。例如,如果您对大量大对象多次调用
Serialize(obj)
,您最终将使用大量未直接释放的内存,但是GC 应该正确处理这个问题,但你应该始终释放你的资源。
我已经尝试过这段代码:
在控制台应用程序中使用以下
Main
方法但是,我的
byte
数组没有你的那么大。 看看这个类似的问题,您可能需要考虑检查出 protobuf-net。了解您打算如何处理序列化数据可能也很有趣,您是否需要将其作为字节数组,或者是否可以将其以 XML 形式写入文本文件?
I don't know how you use that code, but one thing that strikes me is that you don't release your resources. For instance, if you call
Serialize(obj)
a lot of times with a lot of large objects, you will end up having a lot of memory being used that is not released directly, however theGC
should handle that properly, but you should always release your resources.I've tried this piece of code:
With the following
Main
-method in a Console ApplicationHowever, my
byte
-array is not nearly as big as yours. Having a look at this similar issue, you might want to consider checking out protobuf-net.It might also be interesting to know what you are intending to do with the serialized data, do you need it as a
byte
-array or could it just as well be XML written to a text-file?尝试序列化为流(即 FileStream)而不是字节数组。通过这种方式,您可以序列化千兆字节的数据,而不会出现 OutOfMemory 异常。
Try to serialize to a stream (i.e. FileStream) instead of byte array. This way you can serialize gigabytes of data without OutOfMemory exception.
尝试将内存流位置设置为 0,然后仅调用 ToArray()。
问候。
Try to set memory stream position to 0 and after only call ToArray().
Regards.
问题似乎是您期望返回一个 180MB 字节数组。这意味着框架需要找到并分配连续的 180MB 空闲内存来将流数据复制到其中,这通常非常困难 - 因此会出现 OutOfMemoryException。如果您需要继续处理这么多内存,请使用内存流本身(根据需要对其进行读取和写入)来保存缓冲区;否则,直接将其保存到文件(或您需要的任何其他位置,例如通过网络提供服务),而不是使用内存流。
我应该提到的是,内存流也有一个自己的 180MB 数组,因此也遇到了一些麻烦,并且可能在序列化期间导致 OutOfMemory - 如果您可以序列化,它可能会更好(例如,更健壮)将其保存到临时文件。您可能还想考虑更紧凑但可能可读性较差的序列化格式,例如 json、二进制序列化或协议缓冲区。
响应评论:要直接序列化到磁盘,请使用
FileStream
而不是MemoryStream
:The problem seems to be that you're expecting to return a 180MB byte array. That means the framework would need to find and allocate a consecutive 180MB of free memory to copy the stream data into, which is usually quite hard - hence the OutOfMemoryException. If you need to continue handling this amount of memory, use the memory stream itself (reading and writing to it as you need) to hold the buffer; otherwise, save it to a file (or to whatever other place you need it, e.g. serving it over a network) directly instead of using the memory stream.
I should mention that the memory stream has a 180MB array of its own in there as well, so is also in a bit of trouble and could cause OutOfMemory during serialization - it would likely be better (as in, more robust) if you could serialize it to a temporary file. You might also want to consider a more compact - but possibly less readable - serialization format, like json, binary serialization, or protocol buffers.
In response to the comment: to serialize directly to disk, use a
FileStream
instead of aMemoryStream
: