2009-08-18 14 views
8

Sto utilizzando VSTS2008 + C# + .Net 3.5 per eseguire questa applicazione console su x64 Server 2003 Enterprise con memoria fisica 12G.strana eccezione di memoria esaurita durante la serializzazione

Ecco il mio codice e trovo quando si esegue l'istruzione bformatter.Serialize (stream, table), c'è un'eccezione di memoria insufficiente. Ho monitorato l'utilizzo della memoria tramite la scheda Perormance di Task Manager e trovo che viene utilizzata solo la memoria fisica 2G quando viene generata un'eccezione, quindi non dovrebbe essere esaurita la memoria. :-(

Delle idee che cosa è sbagliato Qualsiasi limitazione di .Net serializzazione

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; 
    } 

grazie in anticipo, George

+1

(risposto ai commenti) –

+0

Grazie Marc, la risposta è così grande e mi hanno segnato come risposta. Apprezza se puoi condividere la tua esperienza sulla memoria virtuale qui, http://stackoverflow.com/questions/1297797/windows-32-bit-virtual-memory-page-mapping-issue – George2

risposta

10

Nota:?? DataTable il default è il formato di serializzazione XML che è stato utilizzato in . 1. *, che è incredibilmente inefficiente una cosa da provare è di commutazione per il più recente formato di:

dt.RemotingFormat = System.Data.SerializationFormat.Binary; 

Re out out of memory/2 GB; singoli oggetti .NET (come byte[] dietro a MemoryStream) sono limitati a 2 GB. Forse provare a scrivere su un FileStream?

(edit: no: ha provato che, ancora errori)

Mi chiedo anche se è possibile ottenere risultati migliori (in questo caso) utilizzando table.WriteXml(stream), forse con la compressione GZIP come se lo spazio è un premio.

+0

Ciao Marc, grazie per la tua preziosa risposta . Per la limitazione di byte [] della dimensione 2G, potresti raccomandarmi altri documenti su questo argomento, per favore? Non lo so mai e voglio imparare più sfondi. – George2

+0

"edit: nope: provato, ancora errori" - cosa intendi per provare, ancora errori? Intendi quale metodo ha ancora errori? – George2

+0

Marc, ho provato a usare writexml e funziona. Ma le dimensioni del file dei risultati sono solo 520M. È molto meno di 2G. Come si dimostra che il mio codice originale raggiunge il limite di 2G? – George2

1

1) Il sistema operativo è x64, ma l'app x64 (o anycpu)? In caso contrario, è limitato a 2 GB.

2) Si verifica "presto" o dopo che l'app è in esecuzione da un po 'di tempo (cioè n serializzazioni successive)? Potrebbe essere il risultato di una frammentazione dell'heap di oggetti di grandi dimensioni ...?

+0

Grazie KristoferA, per le tue preoccupazioni, 1. Io costruisco in qualsiasi CPU, quindi dovrebbe essere in grado di consumare più di 2G di memoria fisica, correggere? 2. L'eccezione di memoria insufficiente si verifica dopo 1 minuto, dopo che il metodo MakeParentTable viene restituito correttamente. Quindi penso che non dovrebbe essere il risultato di una frammentazione dell'heap di oggetti di grandi dimensioni? Qualche commento? – George2

1

È interessante notare che in realtà aumenta fino a 3,7 GB prima di dare un errore di memoria qui (Windows 7 x64). Apparentemente, avrebbe bisogno di raddoppiare quell'importo per completarlo.

Dato che l'applicazione utilizza 1.65GB dopo la creazione della tabella, sembra probabile che sia colpire il 2GB byte[] (o qualsiasi singolo oggetto) Limite di Marc Gravell sta parlando di (1.65GB + 2GB ~ = 3.7GB)

In base a questo blog, suppongo che sia possibile allocare la memoria utilizzando WINAPI e scrivere la propria implementazione MemoryStream utilizzando tale. Cioè, se volessi davvero farlo. Oppure scrivi uno usando più di un array ovviamente :)

+0

Hi Thorarin, 1. per la limitazione della dimensione 2G del byte [], voglio saperne di più. Avete altri documenti correlati? 2. Il numero 1,65 GB e 2 GB indica il proprio ingombro nel calcolo? – George2

+1

1,65 GB per DataTable stesso. Tutto quello che ho trovato sul limite di 2 GB (diverso da quello che esiste) è http://blogs.msdn.com/joshwil/archive/2005/08/10/450202.aspx – Thorarin

+0

Grazie Thorarin, 1. Capisco la limitazione di 2 GB. Ma come si dimostra che il 2GB è effettivamente raggiunto? :-) 2. Per la 1.65G come si calcola un numero così preciso? :-) – George2

6

Come già discusso, questo è un problema fondamentale nel tentativo di ottenere blocchi contigui di memoria nella dimensione di Gigabyte.

si sarà limitato da (in difficoltà crescente)

  1. La quantità di memoria indirizzabile
    • dal momento che sono a 64 bit questo sarà si 12GB di memoria fisica, al netto di eventuali buchi richiesto dai dispositivi più eventuali spazi per i file di scambio.
    • Si noti che è necessario eseguire un'applicazione con relevant PE headers that indicate it can run 64bit oppure si eseguirà in WoW64 e solo 4 GB di spazio indirizzo.
    • Si noti inoltre che il target predefinito era changed in 2010, un contentious change.
  2. CLR's limitation that no single object may consume more than 2GB of space.
  3. Ricerca di un blocco contiguo all'interno della memoria disponibile.

Si può scoprire che si esaurisce lo spazio prima del limite CLR del 2 perché il buffer supporto nel flusso si espande in un 'raddoppio' della moda e questo si traduce rapidamente nel buffer viene allocato nel Large Object Heap . Questo heap non viene compattato allo stesso modo degli altri heap (1) e di conseguenza il processo di creazione della dimensione massima teorica del buffer sotto i frammenti 2 del LOH in modo da non riuscire a trovare un blocco contiguo sufficientemente grande prima questo succede.

Così un approccio di mitigazione se siete vicino al limite è quello di impostare la capacità iniziale del torrente in modo tale che esso ha sicuramente spazio sufficiente fin dall'inizio tramite one of the constructors.

Dato che si sta scrivendo sul flusso di memoria come parte di un processo di serializzazione, sarebbe logico utilizzare effettivamente i flussi come previsto e utilizzare solo i dati richiesti.

  • Se si esegue la serializzazione in una posizione basata su file, quindi eseguirne lo streaming direttamente.
  • Se si tratta di dati che va in un database Sql Server considerare l'utilizzo di:
  • Se si serializzare questo in memoria per l'uso in diciamo un confronto quindi prendere in considerazione lo streaming dei dati essendo paragonato e diffidente mentre procedete.
  • Se si sta persistendo in memoria un oggetto per ricrearlo, questo in realtà dovrebbe andare in un file o in un file mappato in memoria. In entrambi i casi il sistema operativo è quindi libero di strutturarlo al meglio (nelle cache dei dischi o nelle pagine che vengono mappate dentro e fuori dalla memoria principale) ed è probabile che faccia un lavoro migliore di quello che la maggior parte delle persone è in grado di fare loro stessi.
  • Se si esegue questa operazione in modo che i dati possano essere compressi, considerare l'utilizzo della compressione di streaming. Qualsiasi flusso di compressione basato su blocchi può essere facilmente convertito in una modalità di streaming con l'aggiunta di padding. Se la tua API di compressione non supporta questo in modo nativo, prendi in considerazione l'uso di uno che fa o scrive il wrapper per farlo.
  • Se si esegue questa operazione per scrivere su un buffer di byte che viene quindi bloccato e passato a una funzione non gestita, utilizzare invece lo UnmanagedMemoryStream, che presenta una possibilità leggermente migliore di allocare un buffer di questo tipo di dimensione ma è ancora non garantito per farlo.

Forse, se ci dite che cosa voi stanno serializzazione di un oggetto di queste dimensioni per potremmo essere in grado di dirvi modi migliori per farlo.


  1. Questo è un dettaglio di implementazione non si dovrebbe fare affidamento su
+0

Grazie ShuggyCoUk, la tua risposta è eccezionale! Ho una domanda correlata qui, apprezzo se puoi aiutare. http://stackoverflow.com/questions/1297797/windows-32-bit-virtual-memory-page-mapping-issue – George2

Problemi correlati