¿Memoria total utilizada por el proceso Python?

266

¿Hay alguna forma para que un programa Python determine cuánta memoria está usando actualmente? He visto discusiones sobre el uso de memoria para un solo objeto, pero lo que necesito es el uso total de memoria para el proceso, de modo que pueda determinar cuándo es necesario comenzar a descartar los datos almacenados en caché.

rwallace
fuente

Respuestas:

303

Aquí hay una solución útil que funciona para varios sistemas operativos, incluidos Linux, Windows 7, etc.

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

En mi instalación actual de Python 2.7 con psutil 5.6.3, la última línea debería ser

print(process.memory_info()[0])

en su lugar (hubo un cambio en la API).

Nota: hazlo pip install psutilsi aún no está instalado.

Basj
fuente
3
psutiles multiplataforma y puede devolver los mismos valores que la psherramienta de línea de comando: pythonhosted.org/psutil/#psutil.Process.memory_info
amos
1
"( psutil) actualmente es compatible con Linux, Windows, OSX, FreeBSD y Sun Solaris, arquitecturas de 32 y 64 bits, con versiones de Python de 2.6 a 3.4" de Documentación
Cecilia
2
¿Por qué este número no coincide con el del explorador de procesos? El número de psutil siempre parece ser mayor en aproximadamente un 10%.
wordsforthewise
39
Tenga en cuenta que psutil no está en la biblioteca estándar
grisaitis
12
Para versiones recientes de psutil, psutil.Process()es equivalente a psutil.Process(os.getpid()). Esa es una cosa menos que debes recordar escribir.
rnorris
209

Para sistemas basados ​​en Unix (Linux, Mac OS X, Solaris), puede usar la getrusage()función desde el módulo de biblioteca estándar resource. El objeto resultante tiene el atributo ru_maxrss, que proporciona el uso máximo de memoria para el proceso de llamada:

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

Los documentos de Python no toman nota de las unidades. Consulte la man getrusage.2página de su sistema específico para verificar el valor de la unidad. En Ubuntu 18.04, la unidad se anota como kilobytes. En Mac OS X, son bytes.

La getrusage()función también se puede asignar resource.RUSAGE_CHILDRENpara obtener el uso de los procesos secundarios y (en algunos sistemas) resource.RUSAGE_BOTHpara el uso total del proceso (propio y secundario).

Si solo le importa Linux, también puede leer el archivo /proc/self/statuso /proc/self/statmcomo se describe en otras respuestas para esta pregunta y también esta .

Nathan Craike
fuente
2
Esta bien, lo haré. No estaba seguro si SO tenía un proceso para fusionar preguntas o qué. La publicación duplicada fue en parte para mostrar a las personas que había una solución de biblioteca estándar en ambas preguntas ... y en parte para el representante. ;) ¿Debo eliminar esta respuesta?
Nathan Craike
66
Mac OS definitivamente devuelve el RSS en bytes, Linux lo devuelve en kilobytes.
Neil
13
Las unidades NO están en kilobytes. Depende de la plataforma, por lo que debe usar resource.getpagesize () para averiguarlo. Los documentos de Python dados ( docs.python.org/2/library/resource.html#resource-usage ) son realmente muy claros al respecto. Es 4096 en mi caja.
Ben Lin
55
@BenLin Esos documentos de Python están claramente equivocados, o hay un error en la versión para Mac. La unidad utilizada por getrusage y el valor devuelto por getpagesize son definitivamente diferentes.
Andrew
77
La pregunta solicitó el uso actual . Tenga en cuenta que este es el uso máximo . (Sigue siendo una respuesta útil, solo advierte a las personas que lo copian y pegan por error.)
Luc
65

En Windows, puede usar WMI ( página de inicio , tienda de quesos ):


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)

En Linux (del libro de cocina de 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
codeape
fuente
14
El código de Windows no me funciona. Este cambio hace:return int(result[0].WorkingSet)
John Fouhy
1
Este código de Windows no me funciona en Windows 7 x64, incluso después de la modificación del comentario de John Fouhy.
Basj
Tengo este error: return [ wmi_object (obj, instance_of, fields) para obj en self._raw_query (wql)] Archivo "C: \ Python27 \ lib \ site-packages \ win32com \ client \ util.py", línea 84, en el siguiente retorno _get_good_object_ (self._iter .next (), resultCLSID = self.resultCLSID) pywintypes.com_error: (-2147217385, 'OLE error 0x80041017', None, None) si alguien puede ayudarme? He ganado 8 x64 pero Python en x32
Radu Vlad
Nota: Actualicé el ejemplo de Windows siguiendo la sugerencia de John Fouhy después de inspeccionar la (última) fuente del módulo wmi. Ver también (1) , (2) .
jedwards
33

En Unix, puede usar la psherramienta para monitorearlo:

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

donde 1347 es una identificación de proceso. Además, el resultado está en MB.

bayer
fuente
8

Uso actual de la memoria del proceso actual en Linux , para Python 2 , Python 3 y pypy , sin importar nada:

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

Lee el archivo de estado del proceso actual, toma todo después VmRSS:, luego toma todo antes de la primera línea nueva (aislando el valor de VmRSS), y finalmente corta los últimos 3 bytes que son un espacio y la unidad (kB).
Para volver, elimina cualquier espacio en blanco y lo devuelve como un número.

Probado en Linux 4.4 y 4.9, pero incluso una versión temprana de Linux debería funcionar: buscando man procy buscando la información en el /proc/$PID/statusarchivo, menciona versiones mínimas para algunos campos (como Linux 2.6.10 para "VmPTE"), pero el "VmRSS "field (que uso aquí) no tiene esa mención. Por lo tanto, supongo que ha estado allí desde una versión anterior.

Luc
fuente
5

Me gusta que , gracias por @bayer. Obtengo una herramienta de recuento de proceso específica, ahora.

# 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

Adjunte mi lista de procesos.

$ 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

Referencia

Chu-Siang Lai
fuente
solo una optimización de código para evitar múltiples tuberíasps aux | awk '/python/{sum+=$6}; END {print sum/1024 " MB"}'
NeronLeVelu
4

Para Python 3.6 y psutil 5.4.5 es más fácil usar la memory_percent()función que se enumera aquí .

import os
import psutil
process = psutil.Process(os.getpid())
print(process.memory_percent())
A.Ametov
fuente
1
esto requiere lib psutil
confiq
4

Aún más fácil de usar que /proc/self/status: /proc/self/statm. Es solo una lista delimitada por espacios de varias estadísticas . No he podido decir si ambos archivos están siempre presentes.

/ proc / [pid] / statm

Proporciona información sobre el uso de la memoria, medida en páginas. Las columnas son:

  • tamaño (1) tamaño total del programa (igual que VmSize en / proc / [pid] / status)
  • Residente (2) Tamaño del conjunto residente (igual que VmRSS en / proc / [pid] / status)
  • shared (3) número de páginas compartidas residentes (es decir, respaldadas por un archivo) (igual que RssFile + RssShmem en / proc / [pid] / status)
  • texto (4) texto (código)
  • biblioteca lib (5) (no utilizada desde Linux 2.6; siempre 0)
  • datos (6) datos + pila
  • dt (7) páginas sucias (sin usar desde Linux 2.6; siempre 0)

Aquí hay un ejemplo 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)

Eso produce una lista que se parece a esto:

0
0
368640
368640
368640
638976
638976
909312
909312
909312

Puede ver que salta unos 300,000 bytes después de aproximadamente 3 asignaciones de 100,000 bytes.

Don Kirkby
fuente
4

A continuación se muestra mi decorador de funciones que permite rastrear cuánta memoria consumió este proceso antes de la llamada a la función, cuánta memoria usa después de la llamada a la función y cuánto tiempo se ejecuta la función.

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

Entonces, cuando tienes alguna función decorada con ella

from utils import track

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

Podrá ver esta salida:

inside list create
list_create: memory before: 45,928,448, after: 46,211,072, consumed: 282,624; exec time: 00:00:00
Ihor B.
fuente
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'])
Pedro Reis
fuente
77
Esto podría mejorarse con alguna explicación de lo que hace y cómo funciona.
ArtOfWarfare
2
Según el gran número devuelto (8 dígitos) y cómo no estoy haciendo mucho de nada, ¿supongo que esto tiene que ser bytes? Por lo tanto, son alrededor de 28.5 MB para una instancia interactiva bastante inactiva. (Wow ... ni siquiera me di cuenta de que el comentario anterior era mío desde hace 4 años ... eso es extraño.)
ArtOfWarfare
3

Para los sistemas Unix, el comando time(/ usr / bin / time) le brinda esa información si pasa -v. Vea a Maximum resident set sizecontinuación, que es la memoria máxima (pico) real (no virtual) que se utilizó durante la ejecución del programa :

$ /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
Charly Empereur-mot
fuente
1
Tenga en cuenta que esto puede fallar si solo intenta usar en timelugar de /usr/bin/time. Ver: askubuntu.com/questions/434289/…
comprado el
1

Usando sh y os para entrar en la respuesta de python bayer.

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

La respuesta está en megabytes.

Newmu
fuente
44
Cabe señalar que `sh 'no es un módulo stdlib. Sin embargo, es instalable con pip.
Jürgen A. Erhard