Memoria totale utilizzata dal processo Python?


266

C'è un modo per un programma Python di determinare quanta memoria sta attualmente utilizzando? Ho visto discussioni sull'utilizzo della memoria per un singolo oggetto, ma ciò di cui ho bisogno è l'utilizzo totale della memoria per il processo, in modo da poter determinare quando è necessario iniziare a scartare i dati memorizzati nella cache.

Risposte:


303

Ecco una soluzione utile che funziona con vari sistemi operativi, inclusi Linux, Windows 7, ecc .:

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

Sulla mia attuale installazione di Python 2.7 con psutil 5.6.3, l'ultima riga dovrebbe essere

print(process.memory_info()[0])

invece (c'è stato un cambiamento nell'API).

Nota: fare pip install psutilse non è ancora installato.


3
psutilè multipiattaforma e può restituire gli stessi valori dello psstrumento da riga di comando: pythonhosted.org/psutil/#psutil.Process.memory_info
amos,

1
"( psutil) attualmente supporta Linux, Windows, OSX, FreeBSD e Sun Solaris, architetture a 32 e 64 bit, con versioni Python da 2,6 a 3,4" dalla Documentazione
Cecilia

2
Perché questo numero non corrisponde a quello in Process Explorer? Il numero di psutil sembra essere sempre maggiore di circa il 10%.
parole per

39
Nota che psutil non è nella libreria standard
grisaitis il

12
Per le versioni recenti di psutil, psutil.Process()è equivalente a psutil.Process(os.getpid()). Questa è una cosa in meno che devi ricordare di digitare.
rnorris,

208

Per i sistemi basati su Unix (Linux, Mac OS X, Solaris), è possibile utilizzare la getrusage()funzione dal modulo libreria standard resource. L'oggetto risultante ha l'attributo ru_maxrss, che fornisce l' utilizzo di memoria di picco per il processo chiamante:

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

I documenti Python non prendono nota delle unità. Fare riferimento alla pagina del proprio sistema specifico man getrusage.2per verificare l'unità per il valore. Su Ubuntu 18.04, l'unità è indicata come kilobyte. Su Mac OS X, sono byte.

La getrusage()funzione può anche essere data resource.RUSAGE_CHILDRENper ottenere l'utilizzo per i processi figlio e (su alcuni sistemi)resource.RUSAGE_BOTH per l'uso totale dei (sé e figlio).

Se ti interessa solo Linux, puoi in alternativa leggere il file /proc/self/statuso /proc/self/statmcome descritto nelle altre risposte per questa domanda e anche questa .


2
Va bene, lo farà. Non ero sicuro se SO avesse un processo per unire domande o cosa. Il post duplicato era in parte per mostrare alle persone che c'era una soluzione di libreria standard su entrambe le domande ... e in parte per il rappresentante. ;) Devo eliminare questa risposta?
Nathan Craike,

6
Mac OS restituisce definitivamente l'RSS in byte, Linux lo restituisce in kilobyte.
Neil,

13
Le unità NON sono in kilobyte. Dipende dalla piattaforma, quindi è necessario utilizzare resource.getpagesize () per scoprirlo. I documenti Python forniti ( docs.python.org/2/library/resource.html#resource-usage ) sono in realtà molto chiari al riguardo. È 4096 nella mia scatola.
Ben Lin,

5
@BenLin Questi documenti di Python sono chiaramente sbagliati o c'è un bug nella versione per Mac. L'unità utilizzata da getrusage e il valore restituito da getpagesize sono decisamente diversi.
Andrew,

6
La domanda ha chiesto l' utilizzo corrente . Si noti che questo è il massimo utilizzo. (Ancora una risposta utile, avvertendo solo le persone che hanno erroneamente copiato e incollato.)
Luc

65

Su Windows, puoi usare WMI ( home page , cheeseshop ):


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)

Su Linux (dal ricettario di 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
Il codice di Windows non funziona per me. Questa modifica fa:return int(result[0].WorkingSet)
John Fouhy il

1
Questo codice di Windows non funziona per me su Windows 7 x64, anche dopo la modifica del commento di John Fouhy.
Basj

Ho questo errore: return [ wmi_object (obj, istanza_di, campi) per obj in self._raw_query (wql)] File "C: \ Python27 \ lib \ site-pacchetti \ win32com \ client \ util.py", riga 84, nel prossimo ritorno _get_good_object_ (self._iter .next (), resultCLSID = self.resultCLSID) pywintypes.com_error: (-2147217385, 'Errore OLE 0x80041017', Nessuno, Nessuno) se qualcuno mi può aiutare? Ho vinto 8 x64 ma pitone su x32
Radu Vlad

Nota: ho aggiornato l'esempio di Windows seguendo il suggerimento di John Fouhy dopo aver ispezionato la (più recente) fonte del modulo wmi. Vedi anche (1) , (2) .
jedwards

33

Su unix, puoi usare lo psstrumento per monitorarlo:

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

dove 1347 è un ID processo. Inoltre, il risultato è in MB.


8

Utilizzo della memoria corrente del processo corrente su Linux , per Python 2 , Python 3 e pypy , senza alcuna importazione:

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())

Legge il file di stato del processo corrente, prende tutto dopo VmRSS:, quindi prende tutto prima della prima riga (isolando il valore di VmRSS) e infine taglia gli ultimi 3 byte che sono uno spazio e l'unità (kB).
Per tornare, rimuove qualsiasi spazio bianco e lo restituisce come un numero.

Testato su Linux 4.4 e 4.9, ma anche una versione precedente di Linux dovrebbe funzionare: cercando man proce cercando le informazioni sul /proc/$PID/statusfile, menziona le versioni minime per alcuni campi (come Linux 2.6.10 per "VmPTE"), ma il "VmRSS "Il campo (che uso qui) non ha tale menzione. Pertanto presumo che sia stato lì fin da una versione precedente.


5

Mi piace che , vi ringrazio per @bayer. Ora ho uno specifico strumento per il conteggio dei processi.

# 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

Allega la mia lista dei processi.

$ 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

Riferimento


solo un'ottimizzazione del codice per evitare multi pipeps aux | awk '/python/{sum+=$6}; END {print sum/1024 " MB"}'
NeronLeVelu

4

Per Python 3.6 e psutil 5.4.5 è più facile usare la memory_percent()funzione elencata qui .

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

1
questo richiede lib psutil
confiq

4

Ancora più facile da usare rispetto /proc/self/status: /proc/self/statm. È solo un elenco delimitato da spazi di diverse statistiche . Non sono stato in grado di dire se entrambi i file sono sempre presenti.

/ Proc / [PID] / statm

Fornisce informazioni sull'utilizzo della memoria, misurate in pagine. Le colonne sono:

  • size (1) dimensione totale del programma (uguale a VmSize in / proc / [pid] / status)
  • residente (2) dimensione set residente (uguale a VmRSS in / proc / [pid] / status)
  • shared (3) numero di pagine condivise residenti (ovvero, supportate da un file) (uguale a RssFile + RssShmem in / proc / [pid] / status)
  • testo (4) testo (codice)
  • libreria lib (5) (non utilizzata da Linux 2.6; sempre 0)
  • data (6) data + stack
  • dt (7) pagine sporche (non utilizzate da Linux 2.6; sempre 0)

Ecco un semplice esempio:

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)

Questo produce un elenco che assomiglia a questo:

0
0
368640
368640
368640
638976
638976
909312
909312
909312

Si può vedere che salta di circa 300.000 byte dopo circa 3 allocazioni di 100.000 byte.


4

Di seguito è riportato il mio decoratore di funzioni che consente di tenere traccia della quantità di memoria consumata da questo processo prima della chiamata di funzione, della quantità di memoria utilizzata dopo la chiamata di funzione e per quanto tempo viene eseguita la funzione.

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

Quindi, quando hai qualche funzione decorata con esso

from utils import track

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

Sarai in grado di vedere questo output:

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
Questo potrebbe essere migliorato con alcune spiegazioni di ciò che fa e di come funziona.
ArtOfWarfare il

2
In base al grande numero restituito (8 cifre) e al modo in cui non sto facendo granché, suppongo che questo debba essere byte? Quindi sono circa 28,5 MB per un'istanza interattiva piuttosto inattiva. (Wow ... non avevo nemmeno realizzato che il commento sopra fosse il mio di 4 anni fa ... è strano.)
ArtOfWarfare

3

Per i sistemi Unix il comando time(/ usr / bin / time) ti dà queste informazioni se passi -v. Vedi di Maximum resident set sizeseguito, che è la memoria massima (di picco) reale (non virtuale) utilizzata durante l'esecuzione del programma :

$ /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
Si noti che ciò potrebbe non riuscire se si tenta di utilizzare timeinvece di /usr/bin/time. Vedi: askubuntu.com/questions/434289/…
acquistato il

1

Usando sh e os per entrare nella risposta di Python Bayer.

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

La risposta è in megabyte.


4
Va notato che "sh" non è un modulo stdlib. Tuttavia è installabile con pip.
Jürgen A. Erhard,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.