web-dev-qa-db-fra.com

Une exception de type «System.OutOfMemoryException» a été levée. Pourquoi?

J'ai une requête dynamique qui renvoie environ 590 000 enregistrements. Il s'exécute avec succès la première fois, mais si je le réexécute, je reçois toujours un System.OutOfMemoryException. Pour quelles raisons cela pourrait-il se produire?

L'erreur se produit ici:

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

Voici le code sur lequel s'exécute le bouton:

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

Il s'exécute avec succès la première fois, mais si je le réexécute, je continue d'obtenir une System.OutOfMemoryException. Pour quelles raisons cela pourrait-il se produire?

Indépendamment de ce que les autres ont dit, l'erreur n'a rien à voir avec l'oubli de supprimer votre DBCommand ou DBConnection, et vous ne corrigerez pas votre erreur en supprimant l'un d'eux.

L'erreur a tout à voir avec votre ensemble de données qui contient près de 600 000 lignes de données. Apparemment, votre ensemble de données consomme plus de 50% de la mémoire disponible sur votre machine. De toute évidence, vous manquerez de mémoire lorsque vous retournerez un autre ensemble de données de la même taille avant que le premier ne soit récupéré. Aussi simple que cela.

Vous pouvez résoudre ce problème de plusieurs manières:

  • Pensez à renvoyer moins d'enregistrements. Personnellement, je ne peux pas imaginer un moment où le retour de 600 000 enregistrements a déjà été utile à un utilisateur. Pour minimiser les enregistrements retournés, essayez:

    • Limiter votre requête aux 1 000 premiers enregistrements. S'il y a plus de 1000 résultats renvoyés par la requête, informez l'utilisateur de restreindre ses résultats de recherche.

    • Si vos utilisateurs insistent vraiment pour voir autant de données à la fois, essayez de paginer les données. N'oubliez pas: Google ne vous montre jamais les 22 milliards de résultats d'une recherche à la fois, il vous montre une vingtaine d'enregistrements à la fois. Google ne détient probablement pas tous les résultats de 22 bajillions en mémoire à la fois, il trouve probablement sa mémoire plus efficace pour requérir sa base de données pour générer une nouvelle page.

  • Si vous avez juste besoin de parcourir les données et que vous n'avez pas besoin d'un accès aléatoire, essayez de renvoyer un datareader à la place. Un lecteur de données ne charge qu'un seul enregistrement en mémoire à la fois.

Si aucune de celles-ci n'est une option, vous devez forcer .NET à libérer la mémoire utilisée par l'ensemble de données avant d'appeler votre méthode à l'aide de l'une de ces méthodes:

  • Supprimez toutes les références à votre ancien ensemble de données. Tout ce qui tient à une référence de votre ensemble de données l'empêchera d'être récupéré par la mémoire.

  • Si vous ne pouvez pas annuler toutes les références à votre ensemble de données, supprimez toutes les lignes de l'ensemble de données et tous les objets liés à ces lignes à la place. Cela supprime les références aux datarows et leur permet d'être mangées par le garbage collector.

Je ne pense pas que vous aurez besoin d'appeler GC.Collect() pour forcer un cycle gen. Non seulement c'est généralement une mauvaise idée d'appeler GC.Collect(), car une pression de mémoire suffisante entraînera l'invocation par le NET du garbage collector.

Remarque: l'appel de Dispose sur votre jeu de données ne libère pas de mémoire, il n'appelle pas non plus le garbage collector, ni ne supprime une référence à votre jeu de données. Dispose est utilisé pour nettoyer les ressources non managées, mais le DataSet n'a pas toutes les ressources non gérées. Il implémente uniquement IDispoable car il est inhérent à MarshalByValueComponent, de sorte que la méthode Dispose sur l'ensemble de données est pratiquement inutile.

43
Juliet

Peut-être que vous ne disposez pas des classes de connexion/résultat précédentes de l'exécution précédente, ce qui signifie qu'elles restent toujours en mémoire.

7
Kieron

De toute évidence, vous ne disposez pas de choses.

Considérez la commande "using" lorsque vous utilisez temporairement des objets qui implémentent IDisposable.

3
Quibblesome

essayez de briser vos données volumineuses autant que possible, car j'ai déjà rencontré nombre de fois ce type de problème. Dans lequel j'ai plus de 10 enregistrements Lakh avec 15 colonnes.

1
Ravi

Où échoue-t-il?

Je suis d'accord que votre problème est probablement que votre ensemble de données de 600 000 lignes est probablement trop grand. Je vois que vous l'ajoutez ensuite à Session. Si vous utilisez l'état de session SQL, il devra également sérialiser ces données.

Même si vous disposez correctement de vos objets, vous aurez toujours au moins 2 copies de cet ensemble de données en mémoire si vous l'exécutez deux fois, une fois en session, une fois en code procédural. Cela ne sera jamais mis à l'échelle dans une application Web.

Faites le calcul, 600 000 lignes, à un guidage égal à 1 à 128 bits par ligne, produiraient 9,6 mégaoctets (600 Ko * 128/8) de données, sans parler de la surcharge de l'ensemble de données.

Réduisez vos résultats.

0
Brian Rudolph