2008-12-10 44 views
16

Tengo una consulta dinámica que devuelve alrededor de 590,000 registros. Funciona con éxito la primera vez, pero si lo vuelvo a ejecutar, sigo obteniendo un System.OutOfMemoryException. ¿Cuáles son algunas de las razones por las que esto podría estar pasando?Excepción de tipo 'System.OutOfMemoryException' fue lanzado. ¿Por qué?

El error está ocurriendo aquí:

public static DataSet GetDataSet(string databaseName,string 
            storedProcedureName,params object[] parameters) 
    { 
     //Creates blank dataset 
     DataSet ds = null; 

     try 
     { 
      //Creates database 
      Database db = DatabaseFactory.CreateDatabase(databaseName); 
      //Creates command to execute 
      DbCommand dbCommand = db.GetStoredProcCommand(storedProcedureName); 
      dbCommand.CommandTimeout = COMMAND_TIMEOUT; 
      //Returns the list of SQL parameters associated with that stored proecdure 
      db.DiscoverParameters(dbCommand); 

      int i = 1; 
      //Loop through the list of parameters and set the values 
      foreach (object parameter in parameters) 
      { 
       dbCommand.Parameters[i++].Value = parameter; 
      } 
      //Retrieve dataset and set to ds 
      ds = db.ExecuteDataSet(dbCommand); 
     } 
      //Check for exceptions 
     catch (SqlException sqle) 
     { 
      throw sqle; 
     } 
     catch (Exception e) 
     { 
      throw e; // Error is thrown here. 
     } 
     //Returns dataset 
     return ds; 
    } 

Aquí está el código se ejecuta en el botón de clic:

protected void btnSearchSBIDatabase_Click(object sender, EventArgs e) 
{ 

     LicenseSearch ls = new LicenseSearch(); 

     DataTable dtSearchResults = new DataTable(); 

     dtSearchResults = ls.Search(); 

     Session["dtSearchResults"] = dtSearchResults; 

     Response.Redirect("~/FCCSearch/SearchResults.aspx"); 
     } 
     else 
      lblResults.Visible = true; 
    } 
+0

¿Podemos ver el código? –

+5

590K filas es un poco excesivo, ¿no crees? – StingyJack

+0

¿No es el problema simplemente que está almacenando DataTable en la sesión y luego, al volver a consultar, tiene ambas variables de sesión y su DataTable original en la memoria? Por cierto, no llames a 'throw e;', llama a 'throw;' ya que de lo contrario tu rastro de pila dirá que el manejador 'catch' generó la excepción, cuando en realidad es el código que lo contiene. –

Respuesta

42

Se ejecuta con éxito la primera vez, pero si lo ejecuto de nuevo, me siguen dando un System.OutOfMemoryException. ¿Qué son algunas de las razones por las que podría estar ocurriendo ?

Independientemente de lo que hayan dicho los otros, el error no tiene nada que ver con olvidarse de eliminar su DBCommand o DBConnection, y no solucionará su error desechando ninguno de ellos.

El error tiene todo que ver con su conjunto de datos que contiene casi 600,000 filas de datos. Aparentemente, su conjunto de datos consume más del 50% de la memoria disponible en su máquina. Claramente, se quedará sin memoria cuando devuelva otro conjunto de datos del mismo tamaño antes de que el primero haya sido recolectado. Simple como eso.

Puede solucionar este problema de varias maneras:

  • considerar volver menos registros. Personalmente, no puedo imaginar un momento en el que devolver los 600K registros haya sido útil para un usuario. Para minimizar los registros devueltos, intente:

    • Limitando su consulta a los primeros 1000 registros. Si hay más de 1000 resultados devueltos de la consulta, informe al usuario para limitar los resultados de búsqueda.

    • Si sus usuarios realmente insisten en ver esa cantidad de datos a la vez, intente buscar los datos. Recuerde: Google nunca muestra los 22 millones de resultados de una búsqueda a la vez, muestra aproximadamente 20 registros a la vez. Es probable que Google no contenga los 22 millones de resultados en la memoria a la vez, probablemente le resulte más eficiente en cuanto a la memoria para volver a consultar su base de datos para generar una página nueva.

  • Si sólo necesita para recorrer los datos y no necesita acceso aleatorio, intenta devolver un datareader lugar. Un lector de datos solo carga un registro en la memoria a la vez.

Si ninguno de ellos es una opción, entonces necesita forzar.NET para liberar la memoria utilizada por el conjunto de datos antes de llamar al método que utiliza uno de estos métodos:

  • eliminar todas las referencias a su antiguo conjunto de datos. Cualquier cosa que se aferre a un refenence de su conjunto de datos evitará que sea reclamado por la memoria.

  • Si no puede anular todas las referencias a su conjunto de datos, borre todas las filas del conjunto de datos y cualquier objeto vinculado a esas filas en su lugar. Esto elimina las referencias a los datarows y les permite ser comidos por el recolector de basura.

no creo que necesita para llamar GC.Collect() para forzar un ciclo de generación. No solo es una mala idea llamar al GC.Collect(), ya que una presión de memoria suficiente provocará que .NET invoque el recolector de basura por sí mismo.

Nota: llamar a Dispose en su conjunto de datos no libera ninguna memoria, ni invoca al recolector de elementos innecesarios, ni elimina una referencia a su conjunto de datos. Dispose se usa para limpiar recursos no administrados, pero DataSet no tiene ningún recurso no administrado. Solo implementa IDispoable porque contiene inherentes de MarshalByValueComponent, por lo que el método Dispose en el conjunto de datos es bastante inútil.

+0

Hola, se devuelven 754600 filas para ser exactos si el usuario no selecciona ningún criterio adicional y ejecuta la consulta. Tengo activada la búsqueda, pero todos los resultados todavía se devuelven al conjunto de datos. – Xaisoft

+0

Me he encontrado con este problema, y ​​me resulta MUCHO más eficiente volver a consultar la base de datos cada vez que un usuario quiere ver una página nueva. Requiere más código, pero no hay otro trabajo alrededor. Utilice este código para ayudarlo a obtener resultados de la página en SQL: http://www.davidhayden.com/blog/dave/archive/2005/12/30/2652.aspx – Juliet

7

Tal vez usted no está disponiendo de las clases de conexión/resultado anterior de la ejecución anterior, lo que significa que todavía están dando vueltas en la memoria.

+0

¿Hay alguna manera de que pueda perfilar esto? – Xaisoft

+0

Puede usar varios perfiles de memoria ... Este es el mejor que he encontrado: http://memprofiler.com/ – Kieron

+0

En ese ejemplo que acaba de publicar, asegúrese de que el comando y la conexión se hayan cerrado/dispuesto – Kieron

3

Obviamente no está eliminando cosas.

Considere el comando "using" cuando se usan temporalmente objetos que implementan IDisposable.

0

¿Dónde falla?

Acepto que su problema es probablemente que su conjunto de datos de 600,000 filas sea probablemente demasiado grande. Veo que luego lo está agregando a la Sesión. Si está utilizando el estado de sesión Sql, tendrá que serializar también esos datos.

Incluso si se deshace de sus objetos correctamente, siempre tendrá al menos 2 copias de este conjunto de datos en la memoria si lo ejecuta dos veces, una vez en sesión, una vez en código de procedimiento. Esto nunca se escalará en una aplicación web.

Hacer los cálculos, 600,000 filas, incluso en 1-128 bit guid por fila daría 9.6 megabytes (600k * 128/8) de solo datos, sin mencionar el conjunto de datos por encima.

Recorte sus resultados.

1

intente romper sus datos de gran tamaño tanto como sea posible porque ya he enfrentado varias veces este tipo de problema. En el que tengo más de 10 registros Lakh con 15 columnas.

Cuestiones relacionadas