12
votes

Intéressant OutofMemoryException avec StressBuilder

J'ai la nécessité de construire continuellement de grandes chaînes dans une boucle et de les enregistrer à la base de données qui donne actuellement occasionnellement un OutofMemoryException . .

Qu'est-ce qui se passe essentiellement ici est de créer une chaîne en utilisant xmlwriter avec stringbuilder en fonction de certaines données. Ensuite, j'appelle une méthode à partir d'une bibliothèque externe qui convertit cette chaîne XML en une autre chaîne. Après cela, la chaîne convertie est enregistrée dans la base de données. Tout cela est fait à plusieurs reprises dans une boucle environ 100 fois pour différentes données.

Les cordes en soi ne sont pas trop grosses (inférieures à 500kbytes chacune) et la mémoire de processus n'augmente pas pendant cette boucle. Mais toujours, je reçois occasionnellement un OutofMemeoryExcpetion dans stringbuilder.append . Fait intéressant, cette exception n'a pas abouti à un crash. Je peux attraper cette exception et continuer la boucle.

Qu'est-ce qui se passe ici? Pourquoi devrais-je obtenir un OutofMorioryException Bien qu'il existe encore assez de mémoire libre disponible dans le système? Est-ce un problème de tas de GC?

Étant donné que je ne peux pas contourner la conversion de toutes ces cordes, que pourrais-je faire pour rendre ce travail de manière fiable? Devrais-je forcer une collection de GC? Devrait mettre un thread.sleep dans la boucle? Devrais-je arrêter d'utiliser stringbuilder ? Devrait simplement réessayer lorsqu'il est confronté à un OutofMemoryException ?


0 commentaires

3 Réponses :


15
votes

Il y a de la mémoire mais pas de segment contigu qui peut gérer la taille de votre constructeur de cordes. Vous devez savoir que chaque fois que le tampon du constructeur de cordes est trop court, sa taille est doublée. Si vous pouvez définir (dans la CTOR), la taille de votre constructeur, c'est mieux. Vous pouvez appeler gc.collect () lorsque vous avez terminé avec une grande collection d'objets.

En réalité, lorsque vous avez une certaine mémoire, IT Generaly montre une mauvaise conception, vous pouvez utiliser le disque dur (fichiers Temp) au lieu de la mémoire, vous ne devez pas répartir la mémoire et encore (essayez de réutiliser des objets / tampons /. ..).

Je vous conseille fortement de lire ce message " Out of Memory "ne fait pas référence à la mémoire physique de Eric Lippert.


5 commentaires

Si j'utilise le même StringBuilder encore et encore, je voudrais utiliser le même segment de mémoire (sauf lorsque le SB doit être agrandir)? Cela ne résoudrait-il pas à tous mes problèmes (très probablement)?


Je ne suis pas sûr, vous devrez essayer de voir si le constructeur de cordes garde sa capacité quand il est effacé ...


Pas totalement inutile: vous pouvez éviter la fragmentation de la mémoire qui semble être le vrai problème ici.


Eh bien, il peut réaffecter mais s'il n'y a pas d'autre SB à la poubelle, ce sera beaucoup plus efficace.


Un SB a un tampon, si vous réutilisez la SB, vous pouvez réutiliser le tampon. Si vous créez une nouvelle SB à chaque fois, vous créez un nouveau tampon à chaque fois et les anciens tampons sont à la poubelle. Même si le constructeur de cordes réutilisé est de taille, il y a beaucoup moins de déchets car il n'y a que des tampons de ce constructeur et non de tous les tampons de tous les constructeurs de chaînes d'opérations précédentes.



3
votes

Essayez de réutiliser l'objet StringBuilder lorsque vous effectuez une génération de données.

après ou avant l'utilisation, réinitialisez simplement la taille du StringBuilder vers 0 et commencez à ajouter. Cela va diminuer le nombre d'allocations et éventuellement rendre la situation de mémoire très rare. P>

Pour illustrer mon point: P>

void MainProgram()
{
    StringBuilder builder = new StringBuilder(2 * 1024); //2 Kb

    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
    PerformOperation(builder);
}

void PerformOperation(StringBuilder builder)
{
    builder.Length = 0;

    //
    // do the work here builder.Append(...);
    //
}


0 commentaires