Wil*_*lka 21 .net serialization
我遇到了在.NET中序列化大量对象的问题.对象图非常大,使用了一些新的数据集,所以我得到:
System.Runtime.Serialization.SerializationException
"The internal array cannot expand to greater than Int32.MaxValue elements."
Run Code Online (Sandbox Code Playgroud)
还有其他人达到这个限制吗?你是怎么解决的?
如果可能的话我仍然可以使用内置的序列化机制会很好,但似乎只需要自己滚动(并保持与现有数据文件的向后兼容性)
对象都是POCO,并且正在使用它们进行序列化BinaryFormatter
.被序列化的每个对象实现ISerializable
选择性地序列化其成员(其中一些在加载期间重新计算).
对于MS来说,这看起来像是一个未解决的问题(详情请参见此处),但它已被解决为Wont Fix.细节是(来自链接):
对于具有超过~1320万个对象的对象图,二进制序列化失败.尝试这样做会导致ObjectIDGenerator.Rehash中出现异常,并引用一个引用Int32.MaxValue的误导性错误消息.
在检查SSCLI源代码中的ObjectIDGenerator.cs后,似乎可以通过在sizes数组中添加其他条目来处理更大的对象图.请参阅以下行:
Run Code Online (Sandbox Code Playgroud)// Table of prime numbers to use as hash table sizes. Each entry is the // smallest prime number larger than twice the previous entry. private static readonly int[] sizes = {5, 11, 29, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877, 205759, 411527, 823117, 1646237, 3292489, 6584983};
但是,如果序列化适用于任何合理大小的对象图,那将是很好的.
Blu*_*kMN 10
我尝试重现这个问题,但即使每个1300多万个对象只有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));
}
}
}
Run Code Online (Sandbox Code Playgroud)
.NET Core 2.1 已修复该问题。我已请求将解决方案向后移植到 .NET Framework 4.8:
https://github.com/Microsoft/dotnet-framework-early-access/issues/46。
如果您认为问题应该得到解决,您可以发表评论,表明这对您也很重要。.NET Core 中的修复方法是为 BinaryFormatter 重用 Dictionary 中存在的素数生成器。
如果您序列化了如此多的对象,并且您不想等待 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>
Run Code Online (Sandbox Code Playgroud)
启用 BinaryFormatter 反序列化修复,该修复最终随 .NET 4.7.2 一起发布。有关这两个问题的更多信息可以在这里找到:
https://aloiskraus.wordpress.com/2017/04/23/the-definitive-serialization-performance-guide/
归档时间: |
|
查看次数: |
10292 次 |
最近记录: |