30 votes

La mémoire peut-elle être nettoyée ?

Je travaille en Delphi 5 (avec FastMM installé) sur un projet Win32, et j'ai récemment essayé de réduire considérablement l'utilisation de la mémoire dans cette application. Jusqu'à présent, j'ai réduit de moitié l'utilisation de la mémoire, mais j'ai remarqué quelque chose en travaillant sur une tâche séparée. Lorsque j'ai réduit l'application, l'utilisation de la mémoire est passée de 45 mégas à 1 méga, ce que j'ai attribué à la pagination sur le disque. Lorsque je l'ai restaurée et que j'ai recommencé à travailler, la mémoire est remontée à 15 mégas. En continuant à travailler, l'utilisation de la mémoire a lentement augmenté à nouveau, et une réduction et une restauration l'ont ramenée à 15 mégas. Donc, à mon avis, lorsque mon code demande au système de libérer la mémoire, celle-ci est toujours conservée par Windows, et la collecte des déchets n'intervient que beaucoup plus tard.

Quelqu'un peut-il confirmer ou infirmer ce genre de comportement ? Est-il possible de nettoyer la mémoire de manière programmatique ? Si je continue à utiliser le programme sans faire ce nettoyage manuel, j'obtiens une erreur de mémoire insuffisante au bout d'un certain temps, et j'aimerais l'éliminer. Merci.

Edit : J'ai trouvé un article sur sur.com qui donne beaucoup d'informations à ce sujet, ainsi que des liens et des données pour d'autres domaines de la gestion de la mémoire.

73voto

Barry Kelly Points 30330

Le gestionnaire des tâches n'affiche pas le total des ressources allouées par Windows à l'application. Ce qu'il affiche (par défaut) est l'ensemble de travail. L'ensemble de travail est un concept qui est conçu pour essayer de minimiser le "thrashing" des fichiers de pages dans des conditions de mémoire restreinte. Il s'agit essentiellement de toutes les pages en mémoire que l'application touche régulièrement. Pour que l'application fonctionne avec une réactivité décente, le système d'exploitation s'efforcera de conserver l'ensemble de travail en mémoire physique.

Partant du principe que l'utilisateur ne se soucie guère de la réactivité des applications minimisées, le système d'exploitation réduit leur ensemble de travail. Cela signifie que, sous la pression de la mémoire physique, les pages de mémoire virtuelle appartenant à ce processus sont plus susceptibles d'être transférées sur le disque (dans le fichier de pages) pour faire de la place.

La plupart des systèmes modernes n'ont pas de problèmes de pagination pour la plupart des applications, la plupart du temps. Une machine qui connaît de graves problèmes de pagination peut être presque indiscernable d'une machine en panne, avec de nombreuses secondes, voire des minutes, qui s'écoulent avant que les applications ne répondent aux entrées de l'utilisateur.

Le comportement que vous observez est donc que Windows réduit l'ensemble de travail lors de la minimisation, puis l'augmente au fur et à mesure que l'application, restaurée, touche de plus en plus de pages. Cela n'a rien à voir avec la collecte des déchets.

Si vous vous intéressez à l'utilisation de la mémoire par une application sous Windows, il n'y a pas de chiffre unique le plus important, mais plutôt une série de chiffres pertinents :

  • Taille virtuelle - c'est la quantité totale d'espace d'adresse réservée par l'application. L'espace d'adressage (c'est-à-dire ce vers quoi pointent les pointeurs) peut être non réservé, réservé ou engagé. La mémoire non réservée peut être allouée à l'avenir, soit par un gestionnaire de mémoire, soit par le chargement de DLL (les DLL doivent aller quelque part en mémoire), etc.

  • Ensemble de travail privé - il s'agit des pages qui sont privées pour cette application (c'est-à-dire qu'elles ne sont pas partagées entre plusieurs applications en cours d'exécution, de sorte qu'une modification apportée à l'une d'elles est vue par toutes), et qui font partie de l'ensemble de travail (c'est-à-dire qu'elles sont fréquemment utilisées par l'application).

  • Ensemble de travail partageable - il s'agit des pages de l'ensemble de travail qui sont partageables, mais qui peuvent ou non être réellement partagées. Par exemple, des DLL ou des paquets (BPL) peuvent être chargés dans l'espace mémoire de l'application. Le code de ces DLL peut potentiellement être partagé entre plusieurs processus, mais si la DLL n'est chargée qu'une seule fois dans une seule application, elle n'est pas réellement partagée. Si la DLL est hautement spécifique à cette application, elle est fonctionnellement équivalente à un ensemble de travail privé.

  • Ensemble de travail partagé - ce sont les pages de l'ensemble de travail qui sont réellement partagées. On pourrait imaginer d'attribuer le "coût" de ces pages pour une application donnée comme étant le montant partagé divisé par le nombre d'applications partageant la page.

  • Octets privés - ce sont les pages de l'espace d'adressage virtuel qui sont engagées par cette application, et qui ne sont pas partagées (ou partageables) entre les applications. Pratiquement toutes les allocations de mémoire effectuées par le gestionnaire de mémoire d'une application aboutissent dans ce pool. Seules les pages qui sont utilisées avec une certaine fréquence doivent faire partie de l'ensemble de travail, donc ce nombre est généralement plus grand que l'ensemble de travail privé. Une augmentation constante du nombre d'octets privés indique soit une fuite de mémoire, soit un algorithme qui fonctionne depuis longtemps et qui nécessite beaucoup d'espace.

Ces chiffres ne représentent pas des ensembles disjoints. Ce sont des façons différentes de résumer l'état de différents types de pages. Par exemple, poste de travail \= ensemble de travail privé + ensemble de travail partageable.

Le choix du chiffre le plus important dépend des contraintes auxquelles vous êtes soumis. Si vous essayez d'effectuer des E/S en utilisant des fichiers mappés en mémoire, la taille virtuelle limitera la quantité de mémoire que vous pouvez consacrer au mappage. Si vous êtes dans un environnement où la mémoire physique est limitée, vous voulez minimiser l'ensemble de travail. Si plusieurs instances de votre application s'exécutent simultanément, vous souhaitez minimiser les octets privés et maximiser les octets partagés. Si vous produisez un tas de DLL et de BPL différents, vous voulez être sûr qu'ils sont réellement partagés, en vous assurant que leurs adresses de chargement ne les font pas entrer en conflit et empêchent le partage.

À propos de SetProcessWorkingSetSize :

Windows gère généralement l'ensemble de travail automatiquement, en fonction de la pression de la mémoire. L'ensemble de travail ne détermine pas si vous allez ou non rencontrer une erreur de type "out of memory" (OOM). L'ensemble de travail est utilisé pour prendre des décisions concernant la pagination, c'est-à-dire ce qu'il faut garder en mémoire et ce qu'il faut laisser sur le disque (dans le cas des DLL) ou sortir sur le disque (autre mémoire engagée). Il n'aura aucun effet à moins qu'il y ait plus de mémoire virtuelle allouée que de mémoire physique dans le système.

Quant à ses effets : si le la limite inférieure est fixée à un niveau élevé Cela signifie que le processus sera hostile aux autres applications et tentera d'accaparer la mémoire dans les situations de pression de la mémoire physique. C'est l'une des raisons pour lesquelles il nécessite un droit de sécurité, PROCESS_SET_QUOTA.

Si le la limite supérieure est fixée à un niveau bas Cela signifie que Windows n'essaiera pas de conserver des pages dans la mémoire physique pour cette application, et qu'il peut en sortir la plupart sur le disque lorsque la pression de la mémoire physique devient élevée.

Dans la plupart des cas, vous ne souhaitez pas modifier les détails de l'ensemble de travail. Il est généralement préférable de laisser le système d'exploitation s'en charger. Cela n'empêchera pas les situations OOM. Celles-ci sont généralement causées par l'épuisement de l'espace d'adressage, parce que le gestionnaire de mémoire n'a pas pu engager plus de mémoire ; ou dans les systèmes avec un espace de fichier de page insuffisant pour sauvegarder la mémoire virtuelle engagée, lorsque l'espace dans le fichier de page est épuisé.

17voto

gabr Points 20458

C'est ce que nous utilisons dans DSiWin32 :

procedure DSiTrimWorkingSet;
var
  hProcess: THandle;
begin
  hProcess := OpenProcess(PROCESS_SET_QUOTA, false, GetCurrentProcessId);
  try
    SetProcessWorkingSetSize(hProcess, $FFFFFFFF, $FFFFFFFF);
  finally CloseHandle(hProcess); end;
end; { DSiTrimWorkingSet }

5voto

François Points 17557

Mettons les choses au clair : FastMM4 ne fait pas fuir la mémoire, votre code pourrait le faire.

Pour en être certain, exécutez cette instruction quelque part dans votre application (où FastMM4 est dans la clause uses et $define ManualLeakReportingControl est définie, dans FastMM4Options.inc par exemple) :

ReportMemoryLeaksOnShutdown := True;

FastMM4 signalera alors à la fin si vous avez oublié de libérer de la mémoire.

Si vous souhaitez en savoir un peu plus, vous pouvez regarder cette vidéo de CodeRage 2 : Combattre les fuites de mémoire pour les nuls

3voto

frogb Points 1508

Après avoir pris connaissance de l'excellente réponse de Barry Kelly, essayez d'analyser votre processus à l'aide de VMMap de Sysinternals, qui se trouve à l'adresse suivante aquí . Il analyse l'utilisation de la mémoire d'un seul processus de manière plus détaillée encore que Process Explorer : "VMMap est l'outil idéal pour les développeurs qui souhaitent comprendre et optimiser l'utilisation des ressources mémoire de leur application." Il dispose également d'un fichier d'aide utile.

2voto

Mason Wheeler Points 52022

Le gestionnaire des tâches ne montre pas ce que votre programme utilise réellement. Il montre le total que le gestionnaire de mémoire a alloué à partir de Windows. Lorsque vous libérez un objet ou que vous désallouez la mémoire allouée dynamiquement, celle-ci est immédiatement retournée au gestionnaire de mémoire (FastMM). Le fait que cette mémoire soit ou non transmise à Windows est une autre question. Le gestionnaire de mémoire aime garder un peu de mémoire supplémentaire afin de ne pas avoir à en demander au système d'exploitation à chaque fois que vous devez créer un nouvel objet. (C'est une bonne chose, et vous ne voulez pas la changer).

Si l'utilisation de la mémoire de votre programme augmente continuellement, au lieu d'atteindre un état stable à un moment donné, vous pouvez regarder autour de vous et voir si vous avez des fuites de mémoire quelque part. Et comme Mark l'a mentionné, Delphi n'utilise pas de ramassage automatique des déchets. Au cas où vous ne le sauriez pas, assurez-vous de libérer vos objets ou de confier leur propriété à quelque chose qui les libérera lorsqu'ils ne seront plus nécessaires.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X