Całkowita pamięć używana przez proces Pythona?

266

Czy istnieje sposób, aby program Python określił, ile pamięci aktualnie używa? Widziałem dyskusje na temat wykorzystania pamięci dla pojedynczego obiektu, ale potrzebuję całkowitego zużycia pamięci dla procesu, aby móc określić, kiedy należy rozpocząć odrzucanie buforowanych danych.

rwallace
źródło

Odpowiedzi:

303

Oto przydatne rozwiązanie, które działa dla różnych systemów operacyjnych, w tym Linux, Windows 7 itp .:

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

W mojej obecnej instalacji Python 2.7 z psutil 5.6.3 ostatnia linia powinna być

print(process.memory_info()[0])

zamiast tego (nastąpiła zmiana w interfejsie API).

Uwaga: zrób, pip install psutiljeśli nie jest jeszcze zainstalowany.

Basj
źródło
3
psutiljest wieloplatformowy i może zwracać te same wartości, co psnarzędzie wiersza poleceń: pythonhosted.org/psutil/#psutil.Process.memory_info
amos
1
„( psutil) obecnie obsługuje systemy Linux, Windows, OSX, FreeBSD i Sun Solaris, zarówno 32-bitowe, jak i 64-bitowe architektury, w wersjach Python od 2.6 do 3.4” z Dokumentacji
Cecilia
2
Dlaczego ta liczba nie odpowiada liczbie w eksploratorze procesów? Liczba z psutil zawsze wydaje się większa o około 10%.
słowa
39
Zauważ, że psutil nie znajduje się w standardowej bibliotece
grisaitis
12
Dla nowszych wersji psutil, psutil.Process()jest równoważne psutil.Process(os.getpid()). To jedna mniejsza rzecz, o której musisz pamiętać.
rnorris
208

W systemach opartych na Uniksie (Linux, Mac OS X, Solaris) możesz użyć getrusage()funkcji ze standardowego modułu bibliotecznego resource. Wynikowy obiekt ma atrybut ru_maxrss, który daje maksymalne użycie pamięci dla procesu wywołującego:

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

Dokumenty Pythona nie odnotowują jednostek. Sprawdź stronę swojego systemu, man getrusage.2aby sprawdzić wartość urządzenia. W systemie Ubuntu 18.04 jednostka jest zapisana w kilobajtach. W systemie Mac OS X są to bajty.

getrusage()Funkcja może być także stosowany resource.RUSAGE_CHILDRENw celu uzyskania serwera dla procesów potomnych, a (na niektórych systemach) resource.RUSAGE_BOTHdla całkowitej (ja i dziecko) Wykorzystanie procesu.

Jeśli zależy Ci tylko na Linuksie, możesz alternatywnie przeczytać plik /proc/self/statuslub /proc/self/statm, jak opisano w innych odpowiedziach na to i to pytanie .

Nathan Craike
źródło
2
OK, zrobi. Nie byłem pewien, czy SO ma proces łączenia pytań czy co. Duplikat postu miał częściowo pokazać ludziom, że istnieje standardowe rozwiązanie biblioteki dla obu pytań ... a częściowo dla przedstawiciela. ;) Czy powinienem usunąć tę odpowiedź?
Nathan Craike,
6
Mac OS zdecydowanie zwraca RSS w bajtach, Linux zwraca go w kilobajtach.
Neil,
13
Jednostki NIE są w kilobajtach. Jest to zależne od platformy, więc musisz się dowiedzieć, korzystając z resource.getpagesize (). Podane dokumenty Pythona ( docs.python.org/2/library/resource.html#resource-usage ) są bardzo jasne na ten temat. W moim pudełku jest 4096.
Ben Lin
5
@BenLin Te dokumenty Pythona są wyraźnie błędne lub występuje błąd w wersji dla komputerów Mac. Jednostka używana przez getrusage i wartość zwracana przez getpagesize są zdecydowanie różne.
Andrew
6
Pytanie o aktualne użycie. Pamiętaj, że jest to maksymalne wykorzystanie. (Wciąż przydatna odpowiedź, ostrzegająca tylko osoby, które omyłkowo ją skopiują i wklejają.)
Luc
65

W systemie Windows możesz korzystać z WMI ( strona główna , sklep z serami ):


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)

W systemie Linux (z książki kucharskiej 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
źródło
14
Kod systemu Windows nie działa dla mnie. Ta zmiana powoduje:return int(result[0].WorkingSet)
John Fouhy,
1
Ten kod systemu Windows nie działa dla mnie w systemie Windows 7 x64, nawet po modyfikacji komentarza Johna Fouhy.
Basj
Mam ten błąd: zwróć [ obiekt_wmi (obj, instancja_of, pola) dla obj w self._raw_query (wql)] Plik „C: \ Python27 \ lib \ site-packages \ win32com \ client \ util.py”, wiersz 84, w następnym zwrocie _get_good_object_ (self._iter .next (), resultCLSID = self.resultCLSID) pywintypes.com_error: (-2147217385, „Błąd OLE 0x80041017”, Brak, Brak) czy ktoś może mi pomóc? Wygrałem 8 x64, ale python na x32
Radu Vlad
Uwaga: zaktualizowałem przykład systemu Windows zgodnie z sugestią Johna Fouhy po sprawdzeniu (najnowszego) źródła modułu wmi. Zobacz także (1) , (2) .
jedwards
33

W systemie Unix możesz użyć tego psnarzędzia do monitorowania:

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

gdzie 1347 to jakiś identyfikator procesu. Wynik jest także w MB.

Bayer
źródło
8

Bieżące użycie pamięci przez bieżący proces w systemie Linux dla Python 2 , Python 3 i pypy , bez żadnych importów:

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

Odczytuje plik statusu bieżącego procesu, bierze wszystko po VmRSS:, następnie bierze wszystko przed pierwszą nową linią (izolując wartość VmRSS), a na koniec odcina ostatnie 3 bajty, które są spacją i jednostką (kB).
Aby zwrócić, usuwa wszelkie białe znaki i zwraca jako liczbę.

Testowany na Linuksie 4.4 i 4.9, ale nawet wczesna wersja Linuksa powinna działać: wyszukiwanie man proci wyszukiwanie informacji w /proc/$PID/statuspliku, wspomina o minimalnych wersjach dla niektórych pól (jak Linux 2.6.10 dla „VmPTE”), ale „VmRSS „w polu (którego tu używam) nie ma takiej wzmianki. Dlatego zakładam, że istnieje tam od wczesnej wersji.

Luc
źródło
5

Podoba mi się , dziękuję za @bayer. Teraz mam specjalne narzędzie do liczenia procesów.

# 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

Załącz moją listę procesów.

$ 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

Odniesienie

Chu-Siang Lai
źródło
po prostu optymalizacja kodu, aby uniknąć wielu ps aux | awk '/python/{sum+=$6}; END {print sum/1024 " MB"}'
potoków
4

W przypadku Python 3.6 i psutil 5.4.5 łatwiej jest korzystać z memory_percent()funkcji wymienionych tutaj .

import os
import psutil
process = psutil.Process(os.getpid())
print(process.memory_percent())
A.Ametov
źródło
1
wymaga to lib psutil
confiq
4

Jeszcze łatwiejsze w użyciu niż /proc/self/status: /proc/self/statm. To tylko rozdzielona spacjami lista kilku statystyk . Nie byłem w stanie stwierdzić, czy oba pliki są zawsze obecne.

/ proc / [pid] / statm

Dostarcza informacji o zużyciu pamięci mierzonej na stronach. Kolumny to:

  • size (1) całkowity rozmiar programu (taki sam jak VmSize w / proc / [pid] / status)
  • resident (2) rozmiar zestawu rezydenta (taki sam jak VmRSS w / proc / [pid] / status)
  • shared (3) liczba rezydentnych udostępnionych stron (tzn. wspieranych przez plik) (to samo co RssFile + RssShmem w / proc / [pid] / status)
  • tekst (4) tekst (kod)
  • biblioteka lib (5) (nieużywana od Linuksa 2.6; zawsze 0)
  • dane (6) dane + stos
  • dt (7) brudne strony (nieużywane od Linuksa 2.6; zawsze 0)

Oto prosty przykład:

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)

To tworzy listę, która wygląda mniej więcej tak:

0
0
368640
368640
368640
638976
638976
909312
909312
909312

Widać, że przeskakuje o około 300 000 bajtów po około 3 przydziałach 100 000 bajtów.

Don Kirkby
źródło
4

Poniżej mój dekorator funkcji, który pozwala śledzić, ile pamięci ten proces zużył przed wywołaniem funkcji, ile pamięci zużywa po wywołaniu funkcji i jak długo funkcja jest wykonywana.

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

Więc kiedy masz jakąś funkcję ozdobioną tym

from utils import track

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

Będziesz mógł zobaczyć ten wynik:

inside list create
list_create: memory before: 45,928,448, after: 46,211,072, consumed: 282,624; exec time: 00:00:00
Ihor B.
źródło
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
źródło
7
Można to poprawić, wyjaśniając, co robi i jak działa.
ArtOfWarfare
2
Na podstawie dużej liczby zwróconych (8 cyfr) i tego, jak niewiele robię, zgaduję, że muszą to być bajty? To około 28,5 MB na raczej bezczynną instancję interaktywną. (Wow ... nawet nie zdawałem sobie sprawy, że powyższy komentarz był mój sprzed 4 lat ... to dziwne.)
ArtOfWarfare
3

Komenda dla systemów uniksowych time(/ usr / bin / time) podaje te informacje, jeśli przejdziesz -v. Zobacz Maximum resident set sizeponiżej, jaka jest maksymalna (szczytowa) rzeczywista (nie wirtualna) pamięć, która została użyta podczas wykonywania programu :

$ /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
źródło
1
Pamiętaj, że może się to nie powieść, jeśli spróbujesz użyć timezamiast /usr/bin/time. Patrz: askubuntu.com/questions/434289/…
zwrócono uwagę
1

Używanie sh i os, aby dostać się do odpowiedzi Pythona Bayera.

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

Odpowiedź jest w megabajtach.

Newmu
źródło
4
Należy zauważyć, że `sh 'nie jest modułem stdlib. Jednak można go zainstalować z pipem.
Jürgen A. Erhard