我的代码是这样的:
[Serializable]
[StructLayout(LayoutKind.Sequential,Pack=1)]
struct Foo
{
public byte Bar;
public Foo(byte b){Bar=b;}
}
public static void Main (string[] args)
{
Foo[] arr = new Foo[1000];
for (int i = 0; i < 1000; i++) {
arr[i]=new Foo(42);
}
var fmt = new BinaryFormatter();
using(FileStream f= File.Create("test.bin")){
fmt.Serialize(f,arr);
}
Console.WriteLine (new FileInfo("test.bin").Length);
}
Foo
结构占用了这么多字节?每个结构开销的9个字节到底是什么?PS: 我正在为中文字符编写查找库(它是关于大约70,000个字符的信息),db4o或其他可嵌入数据库(如sqlite)有点膨胀。我认为以纯字符串格式存储所有信息,这是最友好的,但不太灵活。我想将信息保存在列表中并将它们作为二进制序列化存储到存档中,我选择了DotNetZip进行存档。但序列化开销是一个意想不到的障碍。一个更好的序列化解决方案将是好的,否则我将以纯字符串格式保存信息并通过硬编码解析它。
答案 0 :(得分:14)
Foo结构不是那么“大”,而是你所观察到的是二进制序列化格式本身的开销。此格式包含标题,描述对象图的信息,描述数组的信息,描述类型和汇编信息的字符串等。也就是说,它包含BinaryFormatter.Deserialize
的足够信息,可以返回一个数组Foo就像你期望的那样。
有关详细信息,请参阅以下详细说明格式的规范:http://msdn.microsoft.com/en-us/library/cc236844(PROT.10).aspx
根据您更新的问题进行修改:
如果您希望简单地将结构的内容写入流中,这可以在不安全的上下文中轻松完成(此代码基于您的示例)。
使用小数组写出每个Foo:
unsafe
{
byte[] data = new byte[sizeof(Foo)];
fixed (Foo* ptr = arr)
{
for (int i = 0; i < arr.Length; ++i)
{
Marshal.Copy((IntPtr)ptr + i, data, 0, data.Length);
f.Write(data, 0, data.Length);
}
}
}
或者使用一个足够大的数组来写出所有Foos:
unsafe
{
byte[] data = new byte[sizeof(Foo) * arr.Length];
fixed (Foo* ptr = arr)
{
Marshal.Copy((IntPtr)ptr, data, 0, data.Length);
f.Write(data, 0, data.Length);
}
}
根据您的示例,这将写出1000个字节,每个值为42。
然而,这种方法有一些缺点。如果您熟悉用C语言编写结构,其中一些应该是显而易见的:
BinaryFormatter 可以为您解决这些问题,但会产生您在执行此操作时观察到的空间开销。它旨在以安全的方式在机器之间交换数据。如果您不想使用 BinaryFormatter ,那么您需要自己定义文件格式并自行处理这种格式的读写,或使用最适合您需求的第三方序列化库(我会把这些图书馆的研究留给你)。
答案 1 :(得分:1)
如果您想测量消耗的内存量,可以使用此类代码:
long nTotalMem1 = System.GC.GetTotalMemory(true);
Foo[] arr = new Foo[1000];
for (int i = 0; i < 1000; i++)
{
arr[i] = new Foo(42);
}
long nTotalMem2 = System.GC.GetTotalMemory(true);
Console.WriteLine("Memory consumption: " + (nTotalMem2 - nTotalMem1) + " bytes");
剧透:1012字节。 :)
编辑:可能更可靠的方法是使用Marshal.SizeOf
方法:
Console.WriteLine("Size of one instance: " + Marshal.SizeOf(arr[0]) + " bytes");
这为我返回了1个字节的结果,当向结构添加另一个字段时,它返回2个字节,因此看起来非常可靠。