Mémoire totale utilisée par le processus Python?


266

Existe-t-il un moyen pour un programme Python de déterminer la quantité de mémoire qu'il utilise actuellement? J'ai vu des discussions sur l'utilisation de la mémoire pour un seul objet, mais ce dont j'ai besoin, c'est de l'utilisation totale de la mémoire pour le processus, afin de pouvoir déterminer quand il est nécessaire de commencer à supprimer les données mises en cache.

Réponses:


303

Voici une solution utile qui fonctionne pour différents systèmes d'exploitation, y compris Linux, Windows 7, etc.:

import os
import psutil
process = psutil.Process(os.getpid())
print(process.memory_info().rss)  # in bytes 

Sur mon installation actuelle de Python 2.7 avec psutil 5.6.3, la dernière ligne devrait être

print(process.memory_info()[0])

à la place (il y a eu un changement dans l'API).

Remarque: faites-le pip install psutils'il n'est pas encore installé.


3
psutilest multiplateforme et peut renvoyer les mêmes valeurs que l' psoutil de ligne de commande: pythonhosted.org/psutil/#psutil.Process.memory_info
amos

1
"( psutil) prend actuellement en charge Linux, Windows, OSX, FreeBSD et Sun Solaris, à la fois des architectures 32 bits et 64 bits, avec des versions Python de 2.6 à 3.4" dans Documentation
Cecilia

2
Pourquoi ce nombre ne correspond-il pas à celui de l'explorateur de processus? Le nombre de psutil semble toujours être supérieur d'environ 10%.
mots pour

39
Notez que psutil n'est pas dans la bibliothèque standard
grisaitis

12
Pour les versions récentes de psutil, psutil.Process()est équivalent à psutil.Process(os.getpid()). C'est une chose de moins à retenir pour taper.
rnorris

209

Pour les systèmes basés sur Unix (Linux, Mac OS X, Solaris), vous pouvez utiliser la getrusage()fonction à partir du module de bibliothèque standard resource. L'objet résultant a l'attribut ru_maxrss, qui donne l' utilisation maximale de la mémoire pour le processus appelant:

>>> resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
2656  # peak memory usage (kilobytes on Linux, bytes on OS X)

Les documents Python ne prennent pas en compte les unités. Reportez-vous à la man getrusage.2page de votre système spécifique pour vérifier la valeur de l'unité. Sur Ubuntu 18.04, l'unité est indiquée en kilo-octets. Sur Mac OS X, ce sont des octets.

La getrusage()fonction peut également être donnée resource.RUSAGE_CHILDRENpour obtenir l'utilisation des processus enfants et (sur certains systèmes) resource.RUSAGE_BOTHpour l'utilisation totale (auto et enfant) des processus.

Si vous ne vous souciez que de Linux, vous pouvez également lire le fichier /proc/self/statusou /proc/self/statmcomme décrit dans les autres réponses pour cette question et celle- ci également.


2
D'accord, fera l'affaire. Je ne savais pas si SO avait un processus de fusion des questions ou quoi. Le message en double visait en partie à montrer aux gens qu'il existait une solution de bibliothèque standard sur les deux questions ... et en partie pour le représentant. ;) Dois-je supprimer cette réponse?
Nathan Craike

6
Mac OS renvoie définitivement le RSS en octets, Linux le renvoie en kilo-octets.
Neil

13
Les unités ne sont PAS en kilo-octets. Cela dépend de la plate-forme, vous devez donc utiliser resource.getpagesize () pour le savoir. La documentation Python donnée ( docs.python.org/2/library/resource.html#resource-usage ) est en fait très claire à ce sujet. Il est 4096 dans ma boite.
Ben Lin

5
@BenLin Ces documents Python sont clairement faux, ou il y a un bug sur la version Mac. L'unité utilisée par getrusage et la valeur renvoyée par getpagesize sont définitivement différentes.
Andrew

7
La question demandait l' utilisation actuelle . Notez qu'il s'agit d' une utilisation maximale . (Encore une réponse utile, juste pour avertir les personnes qui le copient / copient par erreur.)
Luc

65

Sous Windows, vous pouvez utiliser WMI ( page d'accueil , fromagerie ):


def memory():
    import os
    from wmi import WMI
    w = WMI('.')
    result = w.query("SELECT WorkingSet FROM Win32_PerfRawData_PerfProc_Process WHERE IDProcess=%d" % os.getpid())
    return int(result[0].WorkingSet)

Sous Linux (à partir du livre de recettes python http://code.activestate.com/recipes/286222/ :

import os
_proc_status = '/proc/%d/status' % os.getpid()

_scale = {'kB': 1024.0, 'mB': 1024.0*1024.0,
          'KB': 1024.0, 'MB': 1024.0*1024.0}

def _VmB(VmKey):
    '''Private.
    '''
    global _proc_status, _scale
     # get pseudo file  /proc/<pid>/status
    try:
        t = open(_proc_status)
        v = t.read()
        t.close()
    except:
        return 0.0  # non-Linux?
     # get VmKey line e.g. 'VmRSS:  9999  kB\n ...'
    i = v.index(VmKey)
    v = v[i:].split(None, 3)  # whitespace
    if len(v) < 3:
        return 0.0  # invalid format?
     # convert Vm value to bytes
    return float(v[1]) * _scale[v[2]]


def memory(since=0.0):
    '''Return memory usage in bytes.
    '''
    return _VmB('VmSize:') - since


def resident(since=0.0):
    '''Return resident memory usage in bytes.
    '''
    return _VmB('VmRSS:') - since


def stacksize(since=0.0):
    '''Return stack size in bytes.
    '''
    return _VmB('VmStk:') - since

14
Le code Windows ne fonctionne pas pour moi. Ce changement:return int(result[0].WorkingSet)
John Fouhy

1
Ce code Windows ne fonctionne pas pour moi sur Windows 7 x64, même après la modification des commentaires de John Fouhy.
Basj

J'ai cette erreur: renvoyer [ wmi_object (obj, instance_of, fields) pour obj dans self._raw_query (wql)] Fichier "C: \ Python27 \ lib \ site-packages \ win32com \ client \ util.py", ligne 84, dans le prochain retour _get_good_object_ (self._iter .next (), resultCLSID = self.resultCLSID) pywintypes.com_error: (-2147217385, 'OLE error 0x80041017', None, None) si quelqu'un peut m'aider? J'ai gagné 8 x64 mais python sur x32
Radu Vlad

Remarque: J'ai mis à jour l'exemple de Windows en suivant la suggestion de John Fouhy après avoir inspecté la (dernière) source du module wmi. Voir également (1) , (2) .
jedwards

33

Sous Unix, vous pouvez utiliser l' psoutil pour le surveiller:

$ ps u -p 1347 | awk '{sum=sum+$6}; END {print sum/1024}'

où 1347 est un identifiant de processus. De plus, le résultat est en Mo.


8

Utilisation actuelle de la mémoire du processus actuel sous Linux , pour Python 2 , Python 3 et pypy , sans aucune importation:

def getCurrentMemoryUsage():
    ''' Memory usage in kB '''

    with open('/proc/self/status') as f:
        memusage = f.read().split('VmRSS:')[1].split('\n')[0][:-3]

    return int(memusage.strip())

Il lit le fichier d'état du processus en cours, prend tout après VmRSS:, puis prend tout avant la première nouvelle ligne (isolant la valeur de VmRSS), et coupe enfin les 3 derniers octets qui sont un espace et l'unité (ko).
Pour revenir, il supprime tout espace et le renvoie sous forme de nombre.

Testé sur Linux 4.4 et 4.9, mais même une première version de Linux devrait fonctionner: en recherchant man procet en recherchant les informations sur le /proc/$PID/statusfichier, il mentionne des versions minimales pour certains champs (comme Linux 2.6.10 pour "VmPTE"), mais le "VmRSS "champ (que j'utilise ici) n'a pas une telle mention. Par conséquent, je suppose qu'il existe depuis une première version.


5

J'aime ça , merci pour @bayer. Je reçois maintenant un outil de comptage de processus spécifique.

# Megabyte.
$ ps aux | grep python | awk '{sum=sum+$6}; END {print sum/1024 " MB"}'
87.9492 MB

# Byte.
$ ps aux | grep python | awk '{sum=sum+$6}; END {print sum " KB"}'
90064 KB

Joignez ma liste de processus.

$ ps aux  | grep python
root       943  0.0  0.1  53252  9524 ?        Ss   Aug19  52:01 /usr/bin/python /usr/local/bin/beaver -c /etc/beaver/beaver.conf -l /var/log/beaver.log -P /var/run/beaver.pid
root       950  0.6  0.4 299680 34220 ?        Sl   Aug19 568:52 /usr/bin/python /usr/local/bin/beaver -c /etc/beaver/beaver.conf -l /var/log/beaver.log -P /var/run/beaver.pid
root      3803  0.2  0.4 315692 36576 ?        S    12:43   0:54 /usr/bin/python /usr/local/bin/beaver -c /etc/beaver/beaver.conf -l /var/log/beaver.log -P /var/run/beaver.pid
jonny    23325  0.0  0.1  47460  9076 pts/0    S+   17:40   0:00 python
jonny    24651  0.0  0.0  13076   924 pts/4    S+   18:06   0:00 grep python

Référence


juste une optimisation de code pour éviter le multi pipeps aux | awk '/python/{sum+=$6}; END {print sum/1024 " MB"}'
NeronLeVelu

4

Pour Python 3.6 et psutil 5.4.5, il est plus facile d'utiliser la memory_percent()fonction répertoriée ici .

import os
import psutil
process = psutil.Process(os.getpid())
print(process.memory_percent())

1
cela nécessite lib psutil
confiq

4

Encore plus facile à utiliser que /proc/self/status: /proc/self/statm. C'est juste une liste séparée par des espaces de plusieurs statistiques . Je n'ai pas pu dire si les deux fichiers sont toujours présents.

/ proc / [pid] / statm

Fournit des informations sur l'utilisation de la mémoire, mesurées en pages. Les colonnes sont:

  • size (1) taille totale du programme (identique à VmSize dans / proc / [pid] / status)
  • résident (2) taille de l'ensemble résident (identique à VmRSS dans / proc / [pid] / status)
  • shared (3) nombre de pages partagées résidentes (c'est-à-dire sauvegardées par un fichier) (identique à RssFile + RssShmem dans / proc / [pid] / status)
  • texte (4) texte (code)
  • bibliothèque lib (5) (inutilisée depuis Linux 2.6; toujours 0)
  • données (6) données + pile
  • dt (7) pages sales (inutilisées depuis Linux 2.6; toujours 0)

Voici un exemple simple:

from pathlib import Path
from resource import getpagesize

PAGESIZE = getpagesize()
PATH = Path('/proc/self/statm')


def get_resident_set_size() -> int:
    """Return the current resident set size in bytes."""
    # statm columns are: size resident shared text lib data dt
    statm = PATH.read_text()
    fields = statm.split()
    return int(fields[1]) * PAGESIZE


data = []
start_memory = get_resident_set_size()
for _ in range(10):
    data.append('X' * 100000)
    print(get_resident_set_size() - start_memory)

Cela produit une liste qui ressemble à ceci:

0
0
368640
368640
368640
638976
638976
909312
909312
909312

Vous pouvez voir qu'il saute d'environ 300 000 octets après environ 3 allocations de 100 000 octets.


4

Ci-dessous, mon décorateur de fonction qui permet de suivre la quantité de mémoire consommée par ce processus avant l'appel de fonction, la quantité de mémoire qu'il utilise après l'appel de fonction et la durée d'exécution de la fonction.

import time
import os
import psutil


def elapsed_since(start):
    return time.strftime("%H:%M:%S", time.gmtime(time.time() - start))


def get_process_memory():
    process = psutil.Process(os.getpid())
    return process.memory_info().rss


def track(func):
    def wrapper(*args, **kwargs):
        mem_before = get_process_memory()
        start = time.time()
        result = func(*args, **kwargs)
        elapsed_time = elapsed_since(start)
        mem_after = get_process_memory()
        print("{}: memory before: {:,}, after: {:,}, consumed: {:,}; exec time: {}".format(
            func.__name__,
            mem_before, mem_after, mem_after - mem_before,
            elapsed_time))
        return result
    return wrapper

Donc, quand vous avez une fonction décorée avec

from utils import track

@track
def list_create(n):
    print("inside list create")
    return [1] * n

Vous pourrez voir cette sortie:

inside list create
list_create: memory before: 45,928,448, after: 46,211,072, consumed: 282,624; exec time: 00:00:00

3
import os, win32api, win32con, win32process
han = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION|win32con.PROCESS_VM_READ, 0, os.getpid())
process_memory = int(win32process.GetProcessMemoryInfo(han)['WorkingSetSize'])

7
Cela pourrait être amélioré avec quelques explications sur ce qu'il fait et comment cela fonctionne.
ArtOfWarfare

2
Sur la base du grand nombre retourné (8 chiffres) et de la façon dont je ne fais pas grand-chose, je suppose que cela doit être des octets? Il s'agit donc d'environ 28,5 Mo pour une instance interactive plutôt inactive. (Wow ... Je ne savais même pas que le commentaire ci-dessus était le mien d'il y a 4 ans ... c'est bizarre.)
ArtOfWarfare

3

Pour les systèmes Unix, la commande time(/ usr / bin / time) vous donne ces informations si vous passez -v. Voir Maximum resident set sizeci - dessous, qui est la mémoire réelle (non virtuelle) maximale (crête) utilisée lors de l'exécution du programme :

$ /usr/bin/time -v ls /

    Command being timed: "ls /"
    User time (seconds): 0.00
    System time (seconds): 0.01
    Percent of CPU this job got: 250%
    Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
    Average shared text size (kbytes): 0
    Average unshared data size (kbytes): 0
    Average stack size (kbytes): 0
    Average total size (kbytes): 0
    Maximum resident set size (kbytes): 0
    Average resident set size (kbytes): 0
    Major (requiring I/O) page faults: 0
    Minor (reclaiming a frame) page faults: 315
    Voluntary context switches: 2
    Involuntary context switches: 0
    Swaps: 0
    File system inputs: 0
    File system outputs: 0
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size (bytes): 4096
    Exit status: 0

1
Notez que cela peut échouer si vous essayez simplement d'utiliser timeau lieu de /usr/bin/time. Voir: askubuntu.com/questions/434289/…
décédé le

1

Utiliser sh et os pour entrer dans la réponse de python bayer.

float(sh.awk(sh.ps('u','-p',os.getpid()),'{sum=sum+$6}; END {print sum/1024}'))

La réponse est en mégaoctets.


4
Il faut noter que `sh 'n'est pas un module stdlib. Il est cependant installable avec pip.
Jürgen A. Erhard
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.