序列化期间奇怪的内存不足异常

Geo*_*ge2 8 .net c# datatable serialization visual-studio-2008

我正在使用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;
    }
Run Code Online (Sandbox Code Playgroud)

乔治,提前谢谢

Mar*_*ell 10

注意:DataTable默认使用1.*中使用的xml序列化格式,这非常低效.要尝试的一件事是切换到更新的格式:

 dt.RemotingFormat = System.Data.SerializationFormat.Binary;
Run Code Online (Sandbox Code Playgroud)

重新出现内存/ 2GB; 各个.NET对象(例如byte[]后面的a MemoryStream)限制为2GB.也许尝试写一个FileStream而不是?

(编辑:nope:尝试过,仍然是错误)

我也想知道你是否可以获得更好的结果(在这种情况下)使用table.WriteXml(stream),如果空间是溢价的话,可能使用GZIP等压缩.


Shu*_*oUk 6

正如已经讨论的那样,这是一个基本的问题,试图获得Gigabyte大小的连续内存块.

你会受到限制(越来越难)

  1. 可寻址内存量
  2. CLR的限制,没有一个单一对象可能消耗比2GB的空间更多.
  3. 在可用内存中查找连续块.

您可以发现在CLR限制之前用完了空间,2因为流中的后备缓冲区以"双倍"方式扩展,这很快导致缓冲区在大对象堆中分配.这个堆没有以与其他堆相同的方式压缩(1),因此2在LOH碎片下构建到缓冲区的理论最大大小的过程使得在此之前找不到足够大的连续块发生.

因此,如果接近极限,则缓解方法是设置流的初始容量,使其从一开始通过其中一个构造函数确实具有足够的空间.

鉴于您作为序列化过程的一部分写入内存流,因此实际使用流并使用所需数据是有意义的.

  • 如果要序列化到某个基于文件的位置,则直接将其流式传输到该位置.
  • 如果这是进入Sql Server数据库的数据,请考虑使用:
  • 如果你在内存中序列化这个用于比较,那么考虑流式传输被比较的数据并随着你的方向进行差异化.
  • 如果你在内存中持久化一个对象来重新创建它,那么这应该是一个文件或一个内存映射文件.在这两种情况下,操作系统都可以自由地构建它(在磁盘缓存或页面中映射进出主存储器),并且它可能比大多数人能够做得更好.他们自己.
  • 如果您这样做以便可以压缩数据,那么请考虑使用流式压缩.通过添加填充,可以相当容易地将任何基于块的压缩流转换为流模式.如果您的压缩API本身不支持这一点,请考虑使用执行或编写包装器的压缩API来执行此操作.
  • 如果您这样做是为了写入一个字节缓冲区,然后将其固定并传递给非托管函数,那么请使用UnmanagedMemoryStream,这样可以更好地分配这种大小的缓冲区,但仍然无法保证这样做.

或许,如果你告诉我们什么你都序列化这种规模的对象,我们也许能够告诉你更好的方法来做到这一点.


  1. 这是您不应该依赖的实现细节