我遇到了在.NET中序列化很多对象的问题。对象图非常大,使用了一些新的数据集,所以我得到了:
System.Runtime.Serialization.SerializationException
"The internal array cannot expand to greater than Int32.MaxValue elements."还有其他人达到这个极限了吗?你是怎么解决的?
如果我仍然可以使用内置的序列化机制(如果可能的话),那将是很好的,但它似乎只需要滚动我自己的(并保持与现有数据文件的向后兼容性)。
这些对象都是波科,正在使用BinaryFormatter进行序列化。每个被序列化的对象都实现ISerializable来有选择地序列化其成员(其中一些在加载期间进行了重新计算)。
对于MS (这里的细节)来说,这似乎是一个悬而未决的问题,但它已经按照惯例得到了解决。详情如下(来自链接):
对于超过1320万个对象的对象图,二进制序列化失败。这样做会导致ObjectIDGenerator.Rehash中的异常,其中包含引用Int32.MaxValue的误导性错误消息。 在审查SSCLI源代码中的ObjectIDGenerator.cs时,似乎可以通过在SSCLI数组中添加其他条目来处理更大的对象图。见以下几行: //用作散列表大小的素数表。每个条目都是//最小素数,大于前一项的两倍。私有静态只读int[] = {5,11,29,47,97,197,397,797,1597,3203,6421,12853,25717,51437,102877,205759,411527,823117,1646237,3292489,6584983}; 但是,如果序列化适用于对象图的任何合理大小,那就太好了。
发布于 2009-04-09 03:37:03
我试着重现这个问题,但是即使每个13+百万对象只有2个字节,代码也要花费很长时间才能运行。因此,我怀疑您不仅可以修复这个问题,而且如果您在自定义ISerialize实现中将数据打包得更好,那么性能也会显著提高。不要让序列化程序在结构中看得那么深,但是在对象图爆炸成几十万个数组元素或更多的时候将其切断(因为如果您有那么多对象,它们可能很小,否则您将无法将它们保存在内存中)。以这个例子为例,它允许序列化程序查看类B和C,但可以手动管理A类的集合:
class Program
{
static void Main(string[] args)
{
C c = new C(8, 2000000);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream ms = new System.IO.MemoryStream();
bf.Serialize(ms, c);
ms.Seek(0, System.IO.SeekOrigin.Begin);
for (int i = 0; i < 3; i++)
for (int j = i; j < i + 3; j++)
Console.WriteLine("{0}, {1}", c.all[i][j].b1, c.all[i][j].b2);
Console.WriteLine("=====");
c = null;
c = (C)(bf.Deserialize(ms));
for (int i = 0; i < 3; i++)
for (int j = i; j < i + 3; j++)
Console.WriteLine("{0}, {1}", c.all[i][j].b1, c.all[i][j].b2);
Console.WriteLine("=====");
}
}
class A
{
byte dataByte1;
byte dataByte2;
public A(byte b1, byte b2)
{
dataByte1 = b1;
dataByte2 = b2;
}
public UInt16 GetAllData()
{
return (UInt16)((dataByte1 << 8) | dataByte2);
}
public A(UInt16 allData)
{
dataByte1 = (byte)(allData >> 8);
dataByte2 = (byte)(allData & 0xff);
}
public byte b1
{
get
{
return dataByte1;
}
}
public byte b2
{
get
{
return dataByte2;
}
}
}
[Serializable()]
class B : System.Runtime.Serialization.ISerializable
{
string name;
List<A> myList;
public B(int size)
{
myList = new List<A>(size);
for (int i = 0; i < size; i++)
{
myList.Add(new A((byte)(i % 255), (byte)((i + 1) % 255)));
}
name = "List of " + size.ToString();
}
public A this[int index]
{
get
{
return myList[index];
}
}
#region ISerializable Members
public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
UInt16[] packed = new UInt16[myList.Count];
info.AddValue("name", name);
for (int i = 0; i < myList.Count; i++)
{
packed[i] = myList[i].GetAllData();
}
info.AddValue("packedData", packed);
}
protected B(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
name = info.GetString("name");
UInt16[] packed = (UInt16[])(info.GetValue("packedData", typeof(UInt16[])));
myList = new List<A>(packed.Length);
for (int i = 0; i < packed.Length; i++)
myList.Add(new A(packed[i]));
}
#endregion
}
[Serializable()]
class C
{
public List<B> all;
public C(int count, int size)
{
all = new List<B>(count);
for (int i = 0; i < count; i++)
{
all.Add(new B(size));
}
}
}发布于 2018-11-19 12:16:04
.NET Core2.1已经解决了这个问题。我请求支持.NET Framework4.8的解决方案:
https://github.com/Microsoft/dotnet-framework-early-access/issues/46。
如果你觉得这个问题应该解决,你可以留下一个评论,这对你来说也很重要。.NET核心的修正是重用字典中的素数生成器,这也适用于BinaryFormatter。
如果序列化了这么多对象,并且不希望等待40分钟才将它们读取回来,请确保将以下内容添加到您的App.Config中:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<!-- Use this switch to make BinaryFormatter fast with large object graphs starting with .NET 4.7.2 -->
<AppContextSwitchOverrides value="Switch.System.Runtime.Serialization.UseNewMaxArraySize=true" />
</runtime>
</configuration>要启用BinaryFormatter反序列化修复程序,最终使用.NET 4.7.2进行了修正。有关这两个问题的更多信息可以在这里找到:
https://aloiskraus.wordpress.com/2017/04/23/the-definitive-serialization-performance-guide/
发布于 2009-04-03 06:01:50
你有没有想过is 32,是2,147,483,647 -20多亿。
您需要16 64的内存来存储指针(假设是64位的机器),更不用说对象本身了。在一台32位的机器上,将8GB的指针数据压缩到3GB左右的最大可用空间,这是一个很好的技巧。
我强烈怀疑您的问题不是对象的数量,而是序列化框架正在进入某种无限循环,因为您的数据结构中有引用循环。
考虑一下这个简单的类:
public class Node
{
public string Name {get; set;}
public IList<Node> Children {get;}
public Node Parent {get; set;}
...
}这个简单的类不能序列化,因为父属性的存在意味着序列化将进入一个无限循环。
由于您已经在实现ISerializable,解决这个问题的方法有75% --您只需要确保从存储的对象图中删除任何循环,而不是存储一个对象树。
经常使用的一种技术是存储引用对象的名称(或id),而不是实际引用,在加载时将名称解析回对象。
https://stackoverflow.com/questions/569127
复制相似问题