我正在使用VSTS2008 + C#+ .Net 3.5在具有12G物理内存的x64 Server 2003 Enterprise上运行此控制台应用程序。
这是我的代码,我发现在执行语句bformatter.Serialize(stream,table)时,存在内存不足异常。我通过任务管理器的Perormance选项卡监视内存使用情况,我发现在抛出异常时只使用2G物理内存,因此不应该内存不足。 : - (
任何想法有什么不对? .Net序列化的任何限制?
static DataTable MakeParentTable()
{
// Create a new DataTable.
System.Data.DataTable table = new DataTable("ParentTable");
// Declare variables for DataColumn and DataRow objects.
DataColumn column;
DataRow row;
// Create new DataColumn, set DataType,
// ColumnName and add to DataTable.
column = new DataColumn();
column.DataType = System.Type.GetType("System.Int32");
column.ColumnName = "id";
column.ReadOnly = true;
column.Unique = true;
// Add the Column to the DataColumnCollection.
table.Columns.Add(column);
// Create second column.
column = new DataColumn();
column.DataType = System.Type.GetType("System.String");
column.ColumnName = "ParentItem";
column.AutoIncrement = false;
column.Caption = "ParentItem";
column.ReadOnly = false;
column.Unique = false;
// Add the column to the table.
table.Columns.Add(column);
// Make the ID column the primary key column.
DataColumn[] PrimaryKeyColumns = new DataColumn[1];
PrimaryKeyColumns[0] = table.Columns["id"];
table.PrimaryKey = PrimaryKeyColumns;
// Create three new DataRow objects and add
// them to the DataTable
for (int i = 0; i <= 5000000; i++)
{
row = table.NewRow();
row["id"] = i;
row["ParentItem"] = "ParentItem " + i;
table.Rows.Add(row);
}
return table;
}
static void Main(string[] args)
{
DataTable table = MakeParentTable();
Stream stream = new MemoryStream();
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Serialize(stream, table); // out of memory exception here
Console.WriteLine(table.Rows.Count);
return;
}
提前谢谢,
乔治
答案 0 :(得分:10)
注意:DataTable
默认使用1. *中使用的xml序列化格式,这非常低效。要尝试的一件事是切换到更新的格式:
dt.RemotingFormat = System.Data.SerializationFormat.Binary;
重新出现内存/ 2GB;各个.NET对象(例如byte[]
后面的MemoryStream
)限制为2GB。也许尝试写一个FileStream
而不是?
(编辑:nope:尝试过,仍然是错误)
我也想知道你是否可以使用table.WriteXml(stream)
获得更好的结果(在这种情况下),如果空间是溢价的话,可能会使用GZIP等压缩。
答案 1 :(得分:6)
正如已经讨论的那样,这是尝试获取Gigabyte大小的连续内存块的基本问题。
你将受到(越来越难)的限制
您可以发现在CLR限制为2
之前空间不足,因为流中的后备缓冲区以“加倍”方式扩展,这很快导致缓冲区在大对象中分配堆。这个堆的压缩方式与其他堆的压缩方式不同(1),因此在2
下构建缓冲区的理论最大大小的过程会使LOH分段,从而无法找到足够的堆在此之前发生了大量的连续区块。
因此,如果您关闭达到限制,则缓解方法是设置流的初始容量,使其从开始到one of the constructors肯定有足够的空间。
鉴于您正在作为序列化过程的一部分写入内存流,因此实际按预期使用流并仅使用所需数据是有意义的。
也许如果你告诉我们你正在序列化这个大小的对象,我们或许可以告诉你更好的方法。
答案 2 :(得分:1)
1)操作系统是x64,但是app x64(或anycpu)?如果没有,它的上限为2Gb。
2)这是在'早期'发生,还是在应用程序运行一段时间后(即后来的n个序列化)?它可能是大对象堆碎片的结果......?
答案 3 :(得分:1)
有趣的是,它实际上达到了3.7GB,然后才给出了内存错误(Windows 7 x64)。 显然,它需要大约两倍才能完成。
鉴于应用程序在创建表后使用1.65GB,看起来它可能达到了2GB byte[]
(或任何单个对象)限制Marc Gravell所说的(1.65GB + 2GB~ = 3.7GB)
基于此blog,我想您可以使用WINAPI分配内存,并使用它编写自己的MemoryStream实现。也就是说,如果你真的想这样做。或者当然使用多个数组写一个:)