Maison  >  Article  >  développement back-end  >  Collecte des déchets .Net et traitement des objets volumineux

Collecte des déchets .Net et traitement des objets volumineux

黄舟
黄舟original
2017-02-17 11:17:371306parcourir

Texte original en anglais : Maoni Stephens, compilé par : Zhao Yukai (@玉凯Sir)

Le ramasse-miettes CLR divise les objets en fonction de la taille de l'espace qu'ils occupent. Il existe une grande différence dans la façon dont les gros objets et les petits objets sont manipulés. Par exemple, la défragmentation de la mémoire - le déplacement d'objets volumineux en mémoire coûte cher. Étudions comment le garbage collector gère les objets volumineux et quel impact potentiel ont les objets volumineux sur les performances du programme.

Tas d'objets volumineux et collecte des déchets

Dans .Net 1.0 et 2.0, si la taille d'un objet dépasse 85 000 octets, il est considéré comme A gros objet. Ce nombre est basé sur l'expérience en matière d'optimisation des performances. Lorsque la taille mémoire demandée par un objet atteint ce seuil, elle sera allouée sur le tas des grands objets. Qu'est-ce que cela signifie? Pour comprendre cela, nous devons comprendre le mécanisme de récupération de place .Net.

Comme la plupart des gens le savent, .Net GC collecte des données par « générations ». Il existe trois générations d'objets dans le programme, la génération 0, la génération 1 et la génération 2. La génération 0 est l'objet le plus jeune et les objets de la génération 2 ont la durée de survie la plus longue. GC collecte les déchets par génération pour des raisons de performances ; les objets seront généralement recyclés dans la génération 0. Par exemple, dans un programme ASP.NET, les objets associés à chaque requête doivent être recyclés à la fin de la requête. Les objets qui n'ont pas été recyclés deviendront des objets de génération 1 ; c'est-à-dire que les objets de génération 1 sont un tampon entre les objets de mémoire résidents et les objets sur le point de mourir.

D'un point de vue générationnel, les gros objets appartiennent aux objets de génération 2, car les gros objets ne sont traités que lors du recyclage de génération 2. Lorsqu'une certaine génération de garbage collection est exécutée, la garbage collection de la jeune génération sera exécutée en même temps. Par exemple : lorsque le garbage collection de 1ère génération est effectué, les objets de la 1ère génération et de la 0ème génération seront collectés en même temps. Lorsque le garbage collection de 2ème génération est effectué, la collecte de la 1ère génération et de la 0ème génération sera collectée. être effectuée. La génération

est l'endroit où le garbage collector distingue les zones de mémoire. Du point de vue du stockage physique, les objets sont alloués sur différents tas gérés. Un tas géré est une zone mémoire allouée par le garbage collector du système d'exploitation (en appelant l'API Windows VirtualAlloc). Lorsque le CLR charge la mémoire, il initialise deux tas gérés, un tas d'objets volumineux (LOH – tas d'objets volumineux) et une petite paire d'objets (SOH – tas de petits objets).

Une demande d'allocation de mémoire consiste à placer l'objet géré sur le tas géré correspondant. Si la taille de l’objet est inférieure à 85000 octets, il sera placé en SOH sinon, il sera placé en LOH ;

Pour SOH, l'objet entrera dans la génération suivante après avoir effectué un garbage collection. C'est-à-dire que si l'objet survivant entre dans la deuxième génération lorsque le garbage collection est effectué pour la première fois, si l'objet n'est toujours pas récupéré après le deuxième garbage collection, il deviendra un objet de deuxième génération ; L'objet est l'objet le plus ancien et n'augmentera pas la génération.

Lorsque le garbage collection est déclenché, le garbage collector défragmente le tas de petits objets et déplace les objets survivants ensemble. Quant au tas d'objets volumineux, en raison du coût élevé du déplacement de la mémoire, l'équipe CLR a choisi de simplement les effacer et de former une liste d'objets recyclés pour répondre à la prochaine demande d'objets volumineux d'utilisation de la mémoire. Les objets poubelles adjacents seront fusionnés en A. bloc de mémoire libre.

Il convient toujours de noter que le tas d'objets volumineux ne sera pas défragmenté avant .Net 4.0, mais cela pourrait être fait dans le futur. Donc, si vous souhaitez allouer des objets volumineux et ne souhaitez pas qu'ils soient déplacés, vous pouvez utiliser l'instruction fixe.

Le schéma suivant montre le recyclage des tas de petits objets SOH



Avant la première collecte des ordures l'image ci-dessus Il y a quatre objets obj0-3 ; après le premier garbage collection, obj1 et obj3 ont été recyclés, et obj2 et obj0 ont été déplacés ensemble avant le deuxième garbage collection, trois objets obj4-6 ont été alloués après le deuxième garbage collection ; , Après avoir effectué le garbage collection pour la première fois, obj2 et obj5 ont été recyclés et obj4 et obj6 ont été déplacés à côté d'obj0.

L'image ci-dessous est un diagramme schématique du recyclage de LOH en tas de gros objets



Vous pouvez voir que Le garbage collection n'est pas effectué. Auparavant, il y avait quatre objets obj0-3 ; après le premier garbage collection de deuxième génération, obj1 et obj2 étaient recyclés. Après le recyclage, les espaces occupés par obj1 et obj2 étaient fusionnés lorsque obj4 demandait de la mémoire. allocation, obj1 était L'espace libéré après recyclage et obj2 lui est alloué en même temps, un fragment de mémoire est laissé ; Si la taille de ce fragment est inférieure à 85 000 octets, alors ce fragment ne pourra plus jamais être utilisé pendant le cycle de vie de ce programme.

S'il n'y a pas suffisamment de mémoire libre sur le tas d'objets volumineux pour accueillir le grand espace objet à demander, le CLR essaiera d'abord de demander de la mémoire au système d'exploitation. Si l'application échoue, elle le fera. déclencher un recyclage de deuxième génération pour tenter de libérer de la mémoire.

Lors du garbage collection de 2e génération, la mémoire inutile peut être restituée au système d'exploitation via VirtualFree. Veuillez consulter l'image ci-dessous pour le processus de retour :



Quand les gros objets seront-ils recyclés ?

Avant de discuter du moment où il faut recycler les gros objets, examinons d’abord quand les opérations ordinaires de collecte des déchets sont effectuées. Le garbage collection se produit dans les circonstances suivantes :

1. L'espace demandé dépasse la taille de la mémoire de la génération 0 ou le seuil du tas d'objets volumineux. La plupart des garbage collection gérés se produisent dans ce cas

. 2 . Lors de l'appel de la méthode GC.Collect dans le code du programme ; si le paramètre GC.MaxGeneration est transmis lors de l'appel de la méthode GC.Collect, le garbage collection de tous les objets de génération sera effectué, y compris le garbage collection du tas d'objets volumineux<.>

3. Lorsque le système d'exploitation n'a pas suffisamment de mémoire, lorsque l'application reçoit une notification de mémoire élevée du système d'exploitation

4. Si l'algorithme de collecte des déchets estime que le recyclage de deuxième génération est efficace, il déclenchera le garbage collection de deuxième génération

5. Chaque génération de tas d'objets a un attribut qui occupe un seuil de taille d'espace. Lorsque vous allouez des objets à une certaine génération, vous augmentez la quantité totale de mémoire proche du seuil. de cette génération, ou allouer des objets qui provoquent cette génération. Lorsque la taille du tas dépasse le seuil du tas, un garbage collection se produit. Par conséquent, lorsque vous allouez de petits objets ou des objets volumineux, cela consommera le seuil du tas de génération 0 ou du tas de gros objets. Lorsque le garbage collector augmente la génération d'objets à la génération 1 ou 2, le seuil des générations 1 et 2 sera consommé. Ces seuils changent dynamiquement pendant l'exécution du programme.

Impact sur les performances du tas d'objets volumineux

Examinons d'abord le coût d'allocation des objets volumineux. Lorsque le CLR alloue de la mémoire pour chaque nouvel objet, il doit s'assurer que la mémoire est effacée et non utilisée par d'autres objets (je donne est effacée). Cela signifie que le coût d'allocation est entièrement contrôlé par le coût de compensation (à moins qu'un garbage collection ne soit déclenché pendant l'allocation). S'il faut 2 cycles pour effacer 1 octet, cela signifie qu'il faut 170 000 cycles pour effacer le plus petit objet de grande taille. Normalement, les gens n'attribuent pas d'objets très volumineux. Par exemple, l'allocation d'un objet de 16 Mo sur une machine à 2 GHz prend environ 16 ms pour effacer la mémoire. Le prix est trop élevé.

Jetons un coup d’œil au coût du recyclage. Comme mentionné précédemment, les objets volumineux sont recyclés avec les objets de 2 générations. Si l'espace occupé par un objet de grande taille ou un objet de deuxième génération dépasse son seuil, le recyclage de l'objet de deuxième génération sera déclenché. Si le recyclage de génération 2 est déclenché parce que le tas d'objets volumineux dépasse le seuil, il n'y a pas beaucoup d'objets dans le tas d'objets de génération 2 lui-même qui peuvent être recyclés. Ce n'est pas un gros problème s'il n'y a pas beaucoup d'objets sur le tas de 2ème génération. Cependant, si le tas de deuxième génération est volumineux et contient de nombreux objets, un recyclage excessif de deuxième génération entraînera des problèmes de performances. Si vous allouez temporairement des objets volumineux, l'exécution du garbage collection prendra beaucoup de temps ; c'est-à-dire que si vous continuez à utiliser des objets volumineux puis à les libérer, cela aura un impact négatif important sur les performances.

Les objets énormes sur le tas des grands objets sont généralement des tableaux (il est rare qu'un objet soit très grand). Si les éléments de l'objet sont des références fortes, le coût sera très élevé ; s'il n'y a pas de références mutuelles entre les éléments, il n'est pas nécessaire de parcourir l'intégralité du tableau lors du garbage collection. Par exemple : utilisez un tableau pour enregistrer les nœuds d'un arbre binaire. Une façon consiste à référencer fortement les nœuds gauche et droit dans le nœud :

class Node
{
Data d;
Node left;
Node right;
}
 
Node[] binaryTree = new Node[num_nodes];

Si num_nodes est un grand nombre, il signifie que chaque nœud Tous nécessite la visualisation d'au moins deux éléments de référence. Une alternative consiste à enregistrer les numéros d'index du tableau des éléments du nœud gauche et droit dans le nœud


class Node
{
Data d;
uint left_index;
uint right_index;
}

Dans ce cas, la relation de référence entre les éléments est supprimé ; vous pouvez obtenir le nœud référencé via binaireTree[left_index]. Le garbage collector n'a plus besoin de consulter les éléments de référence associés lors du garbage collection.

Collecter des données de performances pour les tas d'objets volumineux

Il existe plusieurs façons de collecter des données de performances liées aux tas d'objets volumineux. Avant d'expliquer ces méthodes, expliquons pourquoi vous devez collecter des données de performances liées aux tas d'objets volumineux.

Lorsque vous commencez à collecter des données de performances sur un certain aspect, vous avez peut-être déjà trouvé des preuves que cet aspect provoque un goulot d'étranglement en matière de performances ou vous n'avez peut-être pas recherché tous les aspects et n'avez trouvé aucun problème ;

Les compteurs de performances de la mémoire .Net CLR sont généralement le premier outil à prendre en compte lorsque vous recherchez des problèmes de performances. Les compteurs liés à LOH incluent les collections de génération 2 (nombre de collections de tas de 2 générations) et la taille du tas d'objets volumineux. Les collectes de génération 2 indiquent le nombre d'opérations de récupération de place de génération 2 qui ont eu lieu depuis le démarrage du processus. Le compteur de taille de tas d'objets volumineux affiche la taille actuelle du tas d'objets volumineux, y compris l'espace libre ; ce compteur est mis à jour après chaque opération de garbage collection, et non à chaque allocation de mémoire.

Vous pouvez vous référer à la figure ci-dessous pour observer les données de performances liées à la mémoire .Net CLR dans le compteur de performances Windows


Vous pouvez également interroger les valeurs de ces compteurs via des programmes ; de nombreuses personnes collectent des compteurs de performances via des programmes pour aider à trouver les goulots d'étranglement des performances.

Bien sûr, vous pouvez également utiliser le débogueur winddbg pour observer le gros tas d'objets.

Dernier rappel : jusqu'à présent, le tas d'objets volumineux n'est pas défragmenté dans le cadre du garbage collection, mais il s'agit simplement d'un détail d'implémentation de clr, et le code du programme ne doit pas s'appuyer sur cette fonctionnalité. Si vous voulez vous assurer que l'objet ne sera pas déplacé par le ramasse-miettes, utilisez l'instruction fixe.

Adresse originale : http://www.php.cn/

Ce qui précède est un garbage collection .Net et Concernant le contenu du traitement des objets volumineux, veuillez faire attention au site Web PHP chinois (www.php.cn) pour plus de contenu connexe !


Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn