web-dev-qa-db-fra.com

Un moyen de déterminer l'utilisation "réelle" de la mémoire d'un processus, c'est-à-dire un flux RSS sale privé?

Des outils tels que "ps" et "top" signalent différents types d'utilisation de la mémoire, tels que la taille VM et la taille de l'ensemble résident. Cependant, aucune de ces utilisations n'est la "vraie" utilisation de la mémoire:

  • Le code du programme est partagé entre plusieurs instances du même programme.
  • Le code de programme de bibliothèque partagée est partagé entre tous les processus qui utilisent cette bibliothèque.
  • Certaines applications dérivent des processus et partagent de la mémoire avec elles (par exemple via des segments de mémoire partagée).
  • Le système de mémoire virtuelle rend le rapport de taille VM à peu près inutile).
  • RSS est égal à 0 lorsqu'un processus est échangé, ce qui le rend peu utile.
  • Etc.

J'ai trouvé que le RSS sale privé, tel que rapporté par Linux, est la chose la plus proche de la "vraie" utilisation de la mémoire. Ceci peut être obtenu en additionnant tous les Private_Dirty valeurs dans /proc/somepid/smaps.

Cependant, d'autres systèmes d'exploitation offrent-ils des fonctionnalités similaires? Sinon, quelles sont les alternatives? En particulier, je suis intéressé par FreeBSD et OS X.

45
Hongli

Sous OSX, le moniteur d'activité vous donne en fait une très bonne estimation.

La mémoire privée est certainement une mémoire utilisée uniquement par votre application. Par exemple. la mémoire de pile et toute la mémoire réservée dynamiquement à l'aide de malloc () et de fonctions/méthodes comparables (méthode d'allocation pour Objective-C) est de la mémoire privée. Si vous effectuez un fork, la mémoire privée sera partagée avec votre enfant, mais marquée comme copie sur écriture. Cela signifie que tant qu'une page n'est pas modifiée par l'un ou l'autre des processus (parent ou enfant), elle est partagée entre eux. Dès que l'un ou l'autre processus modifie une page, cette page est copiée avant d'être modifiée. Même si cette mémoire est partagée avec les enfants fork (et elle peut seulement être partagée avec les enfants fork), elle est toujours affichée comme mémoire "privée", car dans le pire des cas, chaque page modifié (tôt ou tard), puis il est à nouveau privé de chaque processus.

La mémoire partagée est soit une mémoire actuellement partagée (les mêmes pages sont visibles dans l'espace de processus virtuel de différents processus), soit susceptible de l'être à l'avenir (par exemple, la mémoire morte, car il n'y a aucune raison de ne pas partager la lecture). -seule mémoire). C'est du moins ainsi que j'ai lu le code source de certains outils de ligne de commande d'Apple. Donc, si vous partagez la mémoire entre les processus à l'aide de mmap (ou un appel comparable qui mappe la même mémoire en plusieurs processus), ce serait de la mémoire partagée. Cependant, le code exécutable lui-même est également de la mémoire partagée, car si une autre instance de votre application est démarrée, il n'y a aucune raison pour qu'elle ne partage pas le code déjà chargé en mémoire (les pages de codes exécutables sont en lecture seule par défaut, sauf si vous exécutez votre dans un débogueur). Ainsi, la mémoire partagée est vraiment de la mémoire utilisée par votre application, tout comme une mémoire privée, mais elle peut en outre être partagée avec un autre processus (ou non, mais pourquoi ne compterait-elle pas pour votre application si elle était partagée?)

La mémoire réelle est la quantité de RAM actuellement "assignée" à votre processus, qu'elle soit privée ou partagée. Cela peut être exactement la somme de privé et partagé, mais ce n'est généralement pas le cas. Votre processus il se peut que la mémoire qui lui est affectée soit supérieure à celle dont elle a besoin actuellement (cela accélère les demandes de mémoire supplémentaire à l'avenir), mais cela ne représente aucune perte pour le système. Si un autre processus a besoin de mémoire et qu'aucune mémoire libre n'est disponible, avant que le système ne commence à échanger , cela prendra cette mémoire supplémentaire loin de votre processus et lui assignera un autre processus (qui est une opération rapide et indolore); à cet effet, votre prochain appel malloc pourrait être un peu plus lent. La mémoire réelle peut également être plus petite que la mémoire privée et physique; c'est car si votre processus demande de la mémoire au système, il ne recevra que de la "mémoire virtuelle". Cette mémoire virtuelle n'est liée à aucune page de mémoire réelle tant que vous ne l'utilisez pas (donc malloc 10 Mo de mémoire, n'en utilisez qu'une octet, votre processus n'obtiendra qu'une seule page, 4096 octets, o f mémoire attribuée - le reste n'est attribué que si vous en avez réellement besoin). La mémoire supplémentaire qui est permutée peut ne pas compter dans la mémoire réelle non plus (pas sûr à ce sujet), mais elle comptera dans la mémoire partagée et privée.

La mémoire virtuelle est la somme de tous les blocs d'adresses considérés comme valides dans l'espace de traitement de vos applications. Ces adresses peuvent être liées à la mémoire physique (qui est à nouveau privée ou partagée), ou elles peuvent ne pas l'être, mais dans ce cas, elles seront liées à la mémoire physique dès que vous utilisez l'adresse. L'accès à des adresses mémoire en dehors des adresses connues provoquera un SIGBUS et votre application se bloquera. Lorsque la mémoire est permutée, l'espace d'adressage virtuel de cette mémoire reste valide et l'accès à ces adresses entraîne le remplacement de la mémoire.

Conclusion:
Si votre application n'utilise pas explicitement ou implicitement la mémoire partagée, la mémoire privée est la quantité de mémoire dont votre application a besoin en raison de la taille de la pile (ou des tailles si elle est multithread) et en raison des appels malloc () que vous avez effectués pour la dynamique. Mémoire. Dans ce cas, vous n'avez pas à vous soucier beaucoup de la mémoire partagée ou réelle.

Si votre application utilise la mémoire partagée, et cela inclut une interface graphique, où la mémoire est partagée entre votre application et le WindowServer par exemple, alors vous pourriez également jeter un œil à la mémoire partagée. Un nombre de mémoire partagée très élevé peut signifier que vous avez actuellement trop de ressources graphiques chargées en mémoire.

La mémoire réelle présente peu d'intérêt pour le développement d'applications. S'il est plus grand que la somme de partagé et privé, cela ne signifie rien d'autre que le fait que le système est paresseux à retirer la mémoire de votre processus. S'il est plus petit, votre processus a demandé plus de mémoire qu'il n'en avait réellement besoin, ce qui n'est pas mauvais non plus, car tant que vous n'utilisez pas toute la mémoire demandée, vous ne "volez" pas la mémoire du système. Si elle est beaucoup plus petite que la somme des ressources partagées et privées, vous ne pouvez envisager de demander moins de mémoire que possible, car vous demandez un peu trop de mémoire (encore une fois, ce n'est pas mauvais, mais cela me dit que votre code n'est pas optimisé pour une utilisation minimale de la mémoire et s'il s'agit de plusieurs plates-formes, d'autres plates-formes peuvent ne pas avoir une gestion de la mémoire aussi sophistiquée, vous pouvez donc préférer allouer de nombreux petits blocs au lieu de quelques gros par exemple, ou libérer de la mémoire beaucoup plus tôt, etc. sur).

Si vous n'êtes toujours pas satisfait de toutes ces informations, vous pouvez obtenir encore plus d'informations. Ouvrez un terminal et exécutez:

Sudo vmmap <pid>

où est l'ID de processus de votre processus. Cela vous montrera des statistiques pour CHAQUE bloc de mémoire dans votre espace de processus avec l'adresse de début et de fin. Il vous indiquera également d'où provient cette mémoire (Un fichier mappé? Mémoire de pile? Mémoire mallocée? Une section __DATA ou __TEXT de votre exécutable?), Sa taille en Ko, les droits d'accès et si elle est privée, partagé ou copie sur écriture. S'il est mappé à partir d'un fichier, il vous donnera même le chemin d'accès au fichier.

Si vous ne voulez que "réel" RAM utilisation, utilisez

Sudo vmmap -resident <pid>

Maintenant, il montrera pour chaque bloc de mémoire la taille virtuelle du bloc de mémoire et sa quantité réellement présente dans la mémoire physique.

À la fin de chaque vidage se trouve également un tableau récapitulatif avec les sommes des différents types de mémoire. Ce tableau ressemble à ceci pour Firefox en ce moment sur mon système:

REGION TYPE             [ VIRTUAL/RESIDENT]
===========             [ =======/========]
ATS (font support)      [   33.8M/   2496K]
CG backing stores       [   5588K/   5460K]
CG image                [     20K/     20K]
CG raster data          [    576K/    576K]
CG shared images        [   2572K/   2404K]
Carbon                  [   1516K/   1516K]
CoreGraphics            [      8K/      8K]
IOKit                   [  256.0M/      0K]
MALLOC                  [  256.9M/  247.2M]
Memory tag=240          [      4K/      4K]
Memory tag=242          [     12K/     12K]
Memory tag=243          [      8K/      8K]
Memory tag=249          [    156K/     76K]
STACK GUARD             [  101.2M/   9908K]
Stack                   [   14.0M/    248K]
VM_ALLOCATE             [   25.9M/   25.6M]
__DATA                  [   6752K/   3808K]
__DATA/__OBJC           [     28K/     28K]
__IMAGE                 [   1240K/    112K]
__IMPORT                [    104K/    104K]
__LINKEDIT              [   30.7M/   3184K]
__OBJC                  [   1388K/   1336K]
__OBJC/__DATA           [     72K/     72K]
__PAGEZERO              [      4K/      0K]
__TEXT                  [  108.6M/   63.5M]
__UNICODE               [    536K/    512K]
mapped file             [  118.8M/   50.8M]
shared memory           [    300K/    276K]
shared pmap             [   6396K/   3120K]

Qu'est-ce que cela nous dit? Par exemple. le binaire Firefox et toutes les bibliothèques qu'il charge contiennent 108 Mo de données ensemble dans leurs sections __TEXT, mais actuellement seulement 63 Mo de celles-ci résident actuellement en mémoire. La prise en charge des polices (ATS) nécessite 33 Mo, mais seulement 2,5 Mo environ sont réellement en mémoire. Il utilise un peu plus de 5 Mo de magasins de sauvegarde CG, CG = Core Graphics, ce sont probablement le contenu des fenêtres, les boutons, les images et autres données qui sont mis en cache pour un dessin rapide. Il a demandé 256 Mo via des appels malloc et actuellement 247 Mo sont vraiment mappés sur des pages mémoire. Il dispose de 14 Mo d'espace réservé pour les piles, mais seulement 248 Ko d'espace de pile est réellement utilisé en ce moment.

vmmap a également un bon résumé au-dessus du tableau

ReadOnly portion of Libraries: Total=139.3M resident=66.6M(48%) swapped_out_or_unallocated=72.7M(52%)
Writable regions: Total=595.4M written=201.8M(34%) resident=283.1M(48%) swapped_out=0K(0%) unallocated=312.3M(52%)

Et cela montre un aspect intéressant de l'OS X: pour la mémoire morte, elle ne joue aucun rôle si elle est permutée ou simplement non allouée; il n'y a que résident et non résident. Pour la mémoire inscriptible, cela fait une différence (dans mon cas, 52% de toute la mémoire demandée n'a jamais été utilisée et n'est pas allouée, 0% de la mémoire a été permutée sur le disque)

42
Mecki

Sous Linux, vous souhaiterez peut-être les nombres PSS (taille de jeu proportionnelle) dans/proc/self/smaps. Le PSS d'un mappage est son RSS divisé par le nombre de processus qui utilisent ce mappage.

9
Justin L.

Top sait comment procéder. Il montre VIRT, RES et SHR par défaut sur Debian Linux. VIRT = SWAP + RES. RES = CODE + DONNÉES. SHR est la mémoire qui peut être partagée avec un autre processus (bibliothèque partagée ou autre mémoire).

De plus, la mémoire "sale" est simplement une mémoire RES qui a été utilisée et/ou qui n'a pas été échangée.

Cela peut être difficile à dire, mais la meilleure façon de comprendre est de regarder un système qui ne change pas. Ensuite, RES - SHR est la mémoire exclusive du processus. Cependant, ce n'est pas une bonne façon de voir les choses, car vous ne savez pas que la mémoire dans SHR est utilisée par un autre processus. Il peut représenter des pages d'objets partagés non écrites qui ne sont utilisées que par le processus.

6
Chris

Tu ne peux vraiment pas.

Je veux dire, la mémoire partagée entre les processus ... allez-vous la compter ou non. Si vous ne le comptez pas, vous vous trompez; la somme de l'utilisation de la mémoire de tous les processus ne sera pas l'utilisation totale de la mémoire. Si vous le comptez, vous allez le compter deux fois - la somme ne sera pas correcte.

Moi, je suis content de RSS. Et sachant que vous ne pouvez pas vraiment vous y fier complètement ...

5
alex

Vous pouvez obtenir des flux RSS propres et privés à partir de/proc/pid/smaps

5
abc

Jetez un oeil à smem. Il vous donnera des informations PSS

http://www.selenic.com/smem/

3
gheese

Utilisez l'appel système mincore (2). Citant la page de manuel:

DESCRIPTION
     The mincore() system call determines whether each of the pages in the
     region beginning at addr and continuing for len bytes is resident.  The
     status is returned in the vec array, one character per page.  Each
     character is either 0 if the page is not resident, or a combination of
     the following flags (defined in <sys/mman.h>):
2

Retravaillé pour être beaucoup plus propre, pour démontrer quelques bonnes pratiques en bash, et en particulier pour utiliser awk au lieu de bc.

find /proc/ -maxdepth 1 -name '[0-9]*' -print0 | while read -r -d $'\0' pidpath; do
  [ -f "${pidpath}/smaps" ] || continue
  awk '!/^Private_Dirty:/ {next;}
       $3=="kB" {pd += $2 * (1024^1); next}
       $3=="mB" {pd += $2 * (1024^2); next}
       $3=="gB" {pd += $2 * (1024^3); next}
       $3=="tB" {pd += $2 * (1024^4); next}
       $3=="pB" {pd += $2 * (1024^5); next}
       {print "ERROR!!  "$0 >"/dev/stderr"; exit(1)}
       END {printf("%10d: %d\n", '"${pidpath##*/}"', pd)}' "${pidpath}/smaps" || break
done

Sur un petit conteneur pratique sur ma machine, avec | sort -n -k 2 pour trier la sortie, cela ressemble à ceci:

        56: 106496
         1: 147456
        55: 155648
2
BMDan

Pour une question qui mentionnait Freebsd, personne surpris n'a encore écrit ceci:

Si vous voulez une sortie de style/proc/PROCESSID/status linux, veuillez procéder comme suit:

mount -t linprocfs none /proc
cat /proc/PROCESSID/status

Au moins dans FreeBSD 7.0, le montage n'a pas été fait par défaut (7.0 est une version beaucoup plus ancienne, mais pour quelque chose d'aussi basique, la réponse était cachée dans une liste de diffusion!)

1
Arvind

Vérifiez-le, c'est le code source de gnome-system-monitor, il pense que la mémoire " vraiment utilisée " par un processus est la somme (info->mem) de la mémoire du serveur X (info->memxserver) et la mémoire inscriptible (info->memwritable), la " Mémoire inscriptible " est les blocs de mémoire qui sont marqués comme "Private_Dirty = "dans / proc/PID/smaps fichier.

Autre que le système Linux, pourrait être différent selon le code de gnome-system-monitor.

static void
get_process_memory_writable (ProcInfo *info)
{
    glibtop_proc_map buf;
    glibtop_map_entry *maps;

    maps = glibtop_get_proc_map(&buf, info->pid);

    gulong memwritable = 0;
    const unsigned number = buf.number;

    for (unsigned i = 0; i < number; ++i) {
#ifdef __linux__
        memwritable += maps[i].private_dirty;
#else
        if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE)
            memwritable += maps[i].size;
#endif
    }

    info->memwritable = memwritable;

    g_free(maps);
}

static void
get_process_memory_info (ProcInfo *info)
{
    glibtop_proc_mem procmem;
    WnckResourceUsage xresources;

    wnck_pid_read_resource_usage (gdk_screen_get_display (gdk_screen_get_default ()),
                                  info->pid,
                                  &xresources);

    glibtop_get_proc_mem(&procmem, info->pid);

    info->vmsize    = procmem.vsize;
    info->memres    = procmem.resident;
    info->memshared = procmem.share;

    info->memxserver = xresources.total_bytes_estimate;

    get_process_memory_writable(info);

    // fake the smart memory column if writable is not available
    info->mem = info->memxserver + (info->memwritable ? info->memwritable : info->memres);
}
1
workplaylifecycle