12
votes

Grands tableaux et fragmentation LOH. Quelle est la convention acceptée?

J'ai une autre question active ici En ce qui concerne des problèmes de mémoire sans espoir qui impliquent éventuellement une fragmentation de LOH parmi d'autres inconnues.

Quelle est ma question maintenant, quelle est la manière acceptée de faire des choses? Si mon application doit être effectuée dans Visual C # et doit faire face à de grandes matrices à la mélodie de l'INT [4000000], comment puis-je pas être condamné par le refus du collecteur des ordures pour faire face au loh ?

Il semblerait que je sois obligé de faire des grandes tableaux globaux et n'utilisez jamais le mot "Nouveau" autour de l'un d'entre eux. Donc, je suis resté avec des tableaux globaux sans scrupules avec des variables "maxindex" au lieu de tableaux de taille soigneusement transmis par des fonctions.

On m'a toujours dit que c'était une mauvaise pratique. Quelle alternative existe-t-il?

Y a-t-il une sorte de fonction à la mélodie de system.gc.collectloh ("sérieusement") ? Existe-t-il un moyen d'externaliser la collecte des ordures à autre chose que system.gc?

Quoi qu'il en soit, quelles sont les règles généralement acceptées pour traiter avec de grandes variables (> 85 Ko)?


0 commentaires

6 Réponses :


7
votes

La première chose qui me vient à l'esprit est de diviser le tableau en plus petits, de sorte qu'ils n'atteignent pas la mémoire nécessaire à la GC pour y mettre le loh. Vous pouvez cracher les tableaux en plus petits de 10 000 dites et construire un objet qui saurait que la matrice à regarder en fonction de l'indexeur que vous passez.

Maintenant, je n'ai pas vu le code, mais je voudrais également remettre en question pourquoi vous avez besoin d'un tableau aussi grand. Je souhaiterais potentiellement refactoriser le code afin que toutes ces informations n'ont pas besoin d'être stockées en mémoire à la fois.


2 commentaires

Ma demande prend des images de taille potentielle 1024x720, convertit qu'en une matrice de "hauteur" basée sur l'intensité de pixels. Ensuite, il faut cette matrice et rend une carte de surface dans OpenGL. Donc, j'ai vraiment besoin de toutes ces données en même temps.


FWIW: Les éléments 10k sont une "bonne valeur seuil" car 10k * 8 octets (objet 64 bits refit) <85k LOH Seuil. Les tableaux de double sont déplacés vers LOH en utilisant des calculs séparés.



5
votes

Vous vous trompez. Vous n'avez pas besoin de havean Array Taille 4000000 et vous n'avez certainement pas besoin d'appeler le collecteur de garbace.

  • Écrivez votre propre implémentation iliste. Comme "Pagedlist"
  • stocker des éléments dans les tableaux de 65536 éléments.
  • Créez un tableau de tableaux pour contenir les pages.

    Cela vous permet d'accéder au fondement tous vos éléments avec une redirection uniquement. Et, comme les matrices individuelles sont plus petites, la fragmentation n'est pas un problème ...

    ... si c'est ... puis réutilisez les pages. Ne les jetez pas, mettez-les sur un "pagéliste" statique et tirez-les d'abord. Tout cela peut être fait de manière transparente dans votre classe.

    La très bonne chose est que cette liste est assez dynamique dans l'utilisation de la mémoire. Vous voudrez peut-être redimensionner le tableau du titulaire (le redirecteur). Même quand non, il s'agit d'environ 512Kbdata par page uniquement.

    Les tableaux de seconde niveau ont essentiellement 64 K par octet - qui est 8 octets pour une classe (512 Ko par page, 256 Ko sur 32 bits) ou 64 Ko par octet de structure.

    Techniquement:

    tour int [] dans int [] []

    Décidez si 32 ou 64 bits sont meilleurs que vous le souhaitez;) Ahve Avantages et inconvénients.

    Traiter avec un grand tableau comme celui-ci est mal vivement dans n'importe quel languauge - si vous aurez, alors ... Fondamentalement ... Allouer au programme Démarrer et ne jamais recréer. Seule une solution.


3 commentaires

Donc, un tableau déchiqueté Int [] [] n'a pas besoin d'utiliser la mémoire continue? Un tableau tel que INT [] se comporte de la même manière?


Je crois que le nouveau Int [] allouerait un bloc contigu. Les matrices déchiquetées sont allouées séparément.


Remarque: la sélection LOH commence à Taille de la mémoire , pas d'élément compte . Le seuil de loh est 85k octets , donc environ 10k (objet d'objet x8 Refs) sur 64 bits. Même avec Int [], c'est seulement ~ 20k. Les deux seuils sont inférieurs à 65 000 éléments proposés, ce qui se retrouverait toujours sur LOH.



30
votes

Premièrement, le collecteur des ordures les collecte le loh, donc ne pas être immédiatement effrayé par sa présence. La LOH est collectée lorsque la génération 2 est collectée.

La différence est que le LOH ne soit pas compacté, ce qui signifie que si vous avez un objet dans une longue durée de vie, vous diviserez efficacement la LOH en deux sections - la zone avant et la zone après cet objet. . Si ce comportement continue de se produire, vous pourriez vous retrouver avec la situation où l'espace entre des objets à longue durée n'est pas suffisamment important pour les affectations ultérieures et .NET doit attribuer de plus en plus de mémoire afin de placer vos objets importants, c'est-à-dire le loh se fragmente.

Maintenant, après cela, le LOH peut réduire de taille si la zone à sa fin est totalement exempte d'objets en direct, le seul problème est donc si vous laissez des objets là-bas pendant une longue période (par exemple, la durée de l'application. ).

à partir de .NET 4.5.1, LOH pourrait être compacté, voir gcsettings.largeObjectHeapCompactionMode propriété.

stratégies pour éviter la fragmentation LOH sont les suivantes:

  • Évitez de créer de gros objets qui traînent. Fondamentalement, cela signifie simplement que de grandes matrices ou des objets qui enveloppent de grandes matrices (telles que la mémoire Mémoire qui enveloppe une matrice d'octet), car rien d'autre n'est que grand (les composants d'objets complexes sont stockés séparément sur le tas, de manière rarement très grande). Agitez également pour les grands dictionnaires et les listes car ils utilisent un tableau en interne.
  • Faites attention aux doubles tableaux - le seuil de ces entrants dans le Lhin est beaucoup, beaucoup plus petit - je ne me souviens pas de la figure exacte mais que c'est seulement quelques milliers.
  • Si vous avez besoin d'une mémoire Mémoire, envisagez de faire une version punkée qui recule sur un certain nombre de baies plus petites plutôt que d'un grand tableau. Vous pouvez également effectuer une version personnalisée de l'IList et de la diatrictaire qui utilise des chunking pour éviter les trucs se retrouver dans le LOH en premier lieu.
  • Évitez d'éviter de très longs appels de télécommandes, car la télécommande utilise une forte utilisation de Mémoramatreams pouvant fragmenter le loh pendant la longueur de l'appel.
  • Faites attention à la chaîne interne - pour une raison quelconque, celles-ci sont stockées comme des pages de la LOH et peuvent provoquer une fragmentation sérieuse si votre application continue de rencontrer de nouvelles chaînes à stagiaire, c'est-à-dire éviter d'utiliser String.Intern à moins que l'ensemble des chaînes ne soit connue de Soyez fini et l'ensemble complet est rencontré tôt dans la vie de l'application. (Voir Ma question précédente .)
  • Utilisez Fils de Strike pour voir ce qui utilise exactement la mémoire Loh. Encore une fois, voir Cette question pour plus de détails sur la façon de faire cela.
  • Considérez Piscine de grandes tableaux .

    Edit: Le seuil de LOH pour les baies doubles semble être de 8k.


4 commentaires

C'est probablement une mauvaise question ... mais comment puis-je obtenir CDB et SOS? Google ne semble pas savoir. Tous les liens que je suive pour "Télécharger CDB" Pointez-moi sur une page de résultat de recherche MSDN "DDK3" entre autres choses qui ne ressemblent pas à CDB.


Téléchargez 'Outils de débogage pour Windows'. CDB est le débogueur de la console et WINDBG est l'équivalent «graphique» (c'est toujours en mode texte mais rendu dans une fenêtre MDI). SOS est livré avec .NET: '% systmeroooo% \ microsoft.net \ framework \ v2.0.50727 \ sos.0.50727 \ sos.dll' (.NET 3 utilise le .NET 2 SOS.DLL, .NET 4 est livré avec sa propre version.)


De plus, vous pouvez apparemment charger SOS de la fenêtre immédiate de Visual Studio (éviter le besoin de CDB), mais je n'ai jamais essayé.


Eh bien ... peut-être que la fragmentation de Loh n'est pas mon problème après tout. Je ne vois pas le mot "libre" à côté de quoi que ce soit ...



0
votes

Ajout d'une élaboration de la réponse ci-dessus, car la question peut survenir. La fragmentation de la LOH dépend non seulement des objets vives depuis longtemps, mais si vous avez la situation qu'il y a plusieurs threads et que chacun d'entre eux crée de grandes listes allant sur le LOH, vous pourriez avoir la situation que le premier fil Doit développer sa liste, mais le prochain bit de la mémoire contiguë est déjà repris par une liste d'un deuxième thread, d'où l'exécution allouera une nouvelle mémoire pour la première liste de threads - laissant derrière un trou plutôt grand. C'est ce qui se passe actuellement sur un projet que j'ai hérité et alors même si le LOH est d'environ 4,5 Mo, l'exécution a un total de 117 Mo de mémoire libre, mais le plus grand segment de mémoire libre est de 28 Mo.

Une autre façon, cela pourrait se produire sans fil de plusieurs threads, c'est si vous avez plus d'une liste d'ajouter à une sorte de boucle dans une sorte de boucle et, car chacun se développe au-delà de la mémoire qui l'alloua initialement, chacun saute l'autre au-delà leurs espaces alloués.

Un lien utile est: https://www.simple-tak.com/dotnet/.net-Framework/the-dangers-of-the-large-Object-heap/

Vous recherchez toujours une solution pour cela, une option peut être d'utiliser une sorte d'objets groupés et de demander à la piscine lorsque vous effectuez le travail. Si vous traitez avec de grandes matrices, une autre option consiste à développer une collection personnalisée. Une collection de collections, de sorte que vous n'avez pas une seule liste énorme, mais la rompre dans des listes plus petites que chacune d'elles évite le loh.


0 commentaires

8
votes

C'est une vieille question, mais je pense que cela n'a pas mal de mettre à jour les réponses avec les modifications introduites dans .NET. Il est maintenant possible de défragmenter le grand tas d'objets. Il est clair que le premier choix devrait être de veiller à ce que les meilleurs choix de conception ont été faits, mais il est agréable d'avoir cette option maintenant.

https://msdn.microsoft.com/en-us/library/xe0c2357 (v = vs.110) .aspx

"En commençant par le .NET Framework 4.5.1, vous pouvez compacter le grand tas d'objets (LOH) en définissant la propriété GCSettings.LargeObjectHeapCompactMode de GClargeObjectHeapCompactMode.Compactonce avant d'appeler la méthode de collecte, comme l'illustre l'exemple suivant." / p>

gcsettings peut être trouvé dans l'espace de noms System.Runtime xxx


1 commentaires

Ceci est très utile - nos applications l'appellent (et un GC induit) après leur grande "recharge de cache" qui permet à .NET GC de récupérer tous les anciens objets mis en cache de longue durée et compacte tous les nouveaux caches dans le Loh. La plupart des caches sont grandes et créées à l'avant / rarement. Cependant, des caches dynamiques qui pourraient se déverser à LOH ne bénéficient pas beaucoup, car il est compliqué de savoir quand un "bon temps" est de demander à .net de compacter le loh.



1
votes

C'est une ancienne question, mais avec .NET Standard 1.1 (.NET CORE, .NET Framework 4.5.1+) Il y a une autre solution possible:

Utilisation de Arraypool dans le paquet System.buffer, nous pouvons mettre des matrices de pool pour éviter ce problème.


0 commentaires