Fragmentation mémoire sous IIS 6
Bonjour, ce second article est consacré à la fragmentation mémoire sous IIS6. Notez cependant qu'il peut aussi s'appliquer à IIS5 et, de façon générale, à toute application utilisant massivement le tas par défaut (« default process heap ») et tournant sous un système d'exploitation antérieur à Windows Vista ou Windows 2008 (Windows 2000, XP, 2003).
L'objet de cet article n'étant pas de décrire le fonctionnement interne du tas, je vous reporte aux liens en fin de page si ce sujet vous intéresse… Je précise aussi que le cas décrit ici concerne la fragmentation d'un tas non managé (non géré par .Net).
Au niveau des symptômes perçus par l'utilisateur, un problème de fragmentation mémoire peut se traduire par des messages d'erreurs variés, des exceptions et même un plantage (violation d'accès) de l'application.
Dans le cadre d'un incident traité récemment – et à l'origine de ce blog – une application ASP affichait un message d'erreur « Pilote ISAM introuvable », message qui n'a pas grand-chose à voir avec un problème de fragmentation… Cependant, l'observation de certains compteurs mémoire («Virtual Bytes») montrait clairement une utilisation mémoire élevée ce qui pouvait amener à suspecter un problème de fuite mémoire (« memory leak »). Pour confirmer cette hypothèse, nous avons donc demandé à notre client de nous fournir un « dump » du processus IIS incriminé (W3WP.EXE) à l'aide de l'utilitaire « Debug Diagnostic ». L'analyse du rapport fourni par « Debug Diagnostics » n'a pas montré de problème de fuite mémoire mais la ligne suivante a attiré notre attention :
Warning |
Detected symptoms of high fragmentation in the following heaps in w3wp.exe_LEA_FR_PID_7892_09_24_2008__05_34_17PM_Manual Dump.dmp0x00080000 (Default process heap - 93,94% Fragmented) |
Heap fragmentation is often caused by one of the following two reasons1. Small heap memory blocks that are leaked (allocated but never freed) over time 2. Mixing long lived small allocations with short lived long allocations Both of these reasons can prevent the NT heap manager from using free memory effeciently since they are spread as small fragments that cannot be used as a single large allocation |
Une analyse manuelle du dump par WinDbg permet de confirmer la fragmentation :
0:000> !heap 00080000 –s
Heap |
Flags |
Reserv (k) |
Commit (k) |
Virt (k) |
Free (k) |
List |
UCR |
Virt |
Lock |
Fast |
00080000 |
00000002 |
974976 |
55896 |
838236 |
9749 |
2096 |
1961 |
0 |
53866 |
L |
Virtual address fragmentation 93 % (1961 uncommited ranges)
Notez le « L » de la dernière colonne qui signifie que le tas est de type « Look Asides ». Sans rentrer dans les détails, cela signifie que la gestion du tas est réalisée assez simplement dans une liste chainée. D'autre gestion de tas sont possibles comme la gestion « LFH » dont nous reparlerons un peu plus loin…
Il existe un utilitaire écrit par John Allen « VAView » qui permet de cartographier la nature des espaces alloués à partir du « dump » d'un processus. Cet utilitaire devrait bientôt être disponible en téléchargement sur Codeplex (nous mettrons à jour cet article dès sa disponibilité). En attendant sa disponibilité, vous pouvez télécharger VA View 2.1 ICI. Voici la carte mémoire VAView pour un dump correspondant à un processus IIS fragmenté :
A la vue de cette carte, deux remarques s'imposent :
- Les zones bleues représentent 1,5 GB (Total/Reserved -> 1523 MB) et correspondent à de la mémoire « réservée ». Dans sa grande majorité, cette mémoire correspond à des blocs mémoires libres qui ont été alloués puis libérés sur un tas (Native Heap/Reserved -> 1397 MB). Il est important de noter que la mémoire « réservée » sur un tas n'est jamais « dé-réservée ». Seule la destruction du tas permet de « dé-réserver » la mémoire et, dans le cas du tas par défaut du processus, la destruction du tas ne se produit qu'à la terminaison du processus.
- les zones bleues sont fragmentées par de petites zones rouges. Un bloc de grande taille ne pourra donc pas être alloué dans un secteur bleu. Il faudra nécessairement réserver un nouveau bloc à partir de la zone blanche. Ce bloc deviendra à son tour un bloc bleu (« réservé »), puis rouge (« utilisé ») puis bleu (libéré mais toujours « réservé) pour être finalement fragmenté bleu/rouge de par la nature des allocations réalisées par l'application. Typiquement, une application qui mixe petites et grosses allocations/libérations produira ce résultat… Au final, le gestionnaire de tas sera incapable de satisfaire une demande d'allocation d'un « gros » bloc ce qui finira par faire échouer l'application d'une façon ou d'une autre.
Voyons maintenant ce que l'on peut proposer en dehors de la réécriture de l'application causant la fragmentation :
La première proposition a été d'utiliser cet article: The "HeapDecommitFreeBlockThreshold" registry key. Malheureusement, les résultats ont été décevants…
La commande « !heap 00080000 –s» nous montre que le tas consommant la quasi-totalité de l'espace disponible est le tas par défaut du processus et il est de type « Look Asides ». Or, Windows XP/2003 fourni un nouveau système de gestion de tas appelé tas à faible fragmentation (« Low Fragmentation Heap » ou LFH). Malheureusement, sous XP/2003, la mécanique LFH n'est pas activée sur le tas par défaut d'un processus. Pour activer LFH, il faut appeler l'API HeapSetInformation en passant le « flag » LFH. Nous avons donc bâti un petit objet ATL/COM exposant une méthode « TurnOnLFH » appelant ce code :
ULONG HeapFragValue = 2;// activer LFH
if(HeapSetInformation( GetProcessHeap(), HeapCompatibilityInformation, &HeapFragValue, sizeof(HeapFragValue) ))
L'objet COM étant instancié au lancement de l'application dans global.asa (Sub Application_OnStart) :
set LFHObj=CreateObject("TURNONLFH.ObjTurnOnLFH")
LFHObj.TurnOnLFH()
Vous trouver cet objet ainsi que le code source associé à la fin de cet article.
Suite à ce changement, le problème de fragmentation a été résolu ! Et, là encore, un graphique VAView est plutôt explicite :
Il est à noter que depuis Windows Vista & Windows 2008, le tas par défaut d'un processus (« default process heap ») est activé en mode LFH.
Pour aller plus loin sur ce sujet vous pouvez consulter ces articles :
- What a heap of – part 1 & part 2
- More on Virtual Memory, Memory Fragmentation and Leaks, and WOW64
- Who is this OutOfMemory guy and why does he make my process crash when I have plenty of memory left?
Nous n'avons parlé ici que de fragmentation liée à la gestion des tas. Il existe d'autres types de fragmentation comme la fragmentation causée par le chargement de modules. Les problématiques de fragmentation/défragmentation n'ont donc pas finies d'alimenter les blogs informatiques…
@ Bientôt
Emmanuel Boersma et L'équipe de support IIS Microsoft France