#Windows : obtenir la consommation mémoire réelle de votre application
Surveiller la consommation mémoire de votre application est une bonne pratique. La plus basse elle est, la plus performante est votre application. Aussi au bout d’une certaine limite, une exception est levée et votre application crashe lamentablement.
La méthode classique de lecture de cette information est d’utiliser la méthode GC.GetTotalMemory :
var usedMemory = GC.GetTotalMemory(true) / (1024L * 1024L) + "Mo";
Une fois le gestionnaire de tâche ou un utilitaire tel que Process Explorer lancé, vous remarquerez que les valeurs ne sont pas bonnes.
En effet, la couche XAML est maintenant en natif dans le SDK Windows et vous n’aurez donc pas la consommation de vos composants natifs et donc pas celle des vues. Celles-ci représentent souvent une grosse partie de votre consommation mémoire.
Obtenir les bonnes informations
Pour obtenir les informations complètes, on va donc utiliser la méthode GetProcessMemoryInfo de la DLL psapi à grand coup de DllImport :
- On définit dans le code les différents objets et méthodes manipulées,
- On récupère l’identifiant de processus de notre application avec la méthode GetCurrentProcess.
- On récupère les informations d’utilisation mémoire en utilisant GetProcessMemoryInfo
Voici la définition des objets manipulés :
[DllImport("kernel32.dll")] private static extern IntPtr GetCurrentProcess(); [StructLayout(LayoutKind.Sequential, Size = 40)] private struct PROCESS_MEMORY_COUNTERS { public uint cb; public uint PageFaultCount; public uint PeakWorkingSetSize; public uint WorkingSetSize; public uint QuotaPeakPagedPoolUsage; public uint QuotaPagedPoolUsage; public uint QuotaPeakNonPagedPoolUsage; public uint QuotaNonPagedPoolUsage; public uint PagefileUsage; public uint PeakPagefileUsage; } [DllImport("psapi.dll", SetLastError = true)] static extern bool GetProcessMemoryInfo(IntPtr hProcess, out PROCESS_MEMORY_COUNTERS counters, uint size);
Et le code de lecture des informations et alors très simple car il suffit d’appeler les deux méthodes.
Ici j’affiche le working set et les bytes privés (pour connaitre la différence entre ces deux valeurs suivez ce lien) :
var currentProcessHandle = GetCurrentProcess(); PROCESS_MEMORY_COUNTERS memoryCounters; memoryCounters.cb = (uint)Marshal.SizeOf<PROCESS_MEMORY_COUNTERS>(); if (GetProcessMemoryInfo(currentProcessHandle, out memoryCounters, memoryCounters.cb)) { _targetTextBlock.Text = "MAN " + GC.GetTotalMemory(true) / (1024L * 1024L) + "Mo" + Environment.NewLine + "PFU " + memoryCounters.PagefileUsage / (1024L * 1024L) + "Mo" + Environment.NewLine + "WS " + memoryCounters.WorkingSetSize / (1024L * 1024L) + "Mo"; }
Pas de publication sur le Store
Bien sûr, on est sur un cas d’usage interdit par le Store lors de la publication et il faudra bien faire attention d’enlever ces méthodes lors de la publication finale du package. Cela peut être fait facilement en utilisant des directives de pré-compilation.
Bonus : afficher cette valeur au dessus de votre UI
Un petit bonus qui peut être pratique : voici le code créant une Popup qui sera affichée au dessus de votre application pour afficher les différentes consommations. J’utilise un DispatcherTimer pour rafraichir ces valeurs toutes les secondes.
//création d'un timer var dt = new DispatcherTimer(); dt.Interval = TimeSpan.FromSeconds(1); IntPtr currentProcessHandle = GetCurrentProcess(); dt.Tick += (_, __) => CalculateMemory(currentProcessHandle); dt.Start(); // création d'une popup var p = new Popup(); _targetTextBlock = new TextBlock() { FontSize = 16, Foreground = new SolidColorBrush(Colors.Black) }; p.Child = new Border { Background = new SolidColorBrush(Colors.White), Child = _targetTextBlock, }; // afficahge de la popup p.IsOpen = true; //calcul initial CalculateMemory(currentProcessHandle);
Et le rendu final :
Bon code !
Commentaires