Gesamtspeicher, der vom Python-Prozess verwendet wird?

265

Gibt es eine Möglichkeit für ein Python-Programm, festzustellen, wie viel Speicher es derzeit verwendet? Ich habe Diskussionen über die Speichernutzung für ein einzelnes Objekt gesehen, aber ich benötige die gesamte Speichernutzung für den Prozess, damit ich feststellen kann, wann es erforderlich ist, zwischengespeicherte Daten zu verwerfen.

rwallace
quelle

Antworten:

301

Hier ist eine nützliche Lösung, die für verschiedene Betriebssysteme funktioniert, einschließlich Linux, Windows 7 usw.:

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

Bei meiner aktuellen Python 2.7-Installation mit psutil 5.6.3 sollte die letzte Zeile stehen

print(process.memory_info()[0])

stattdessen (es gab eine Änderung in der API).

Hinweis: Tun pip install psutilSie dies, wenn es noch nicht installiert ist.

Basj
quelle
3
psutilist plattformübergreifend und kann dieselben Werte wie das psBefehlszeilentool zurückgeben: pythonhosted.org/psutil/#psutil.Process.memory_info
amos
1
"( psutil) unterstützt derzeit Linux, Windows, OSX, FreeBSD und Sun Solaris, sowohl 32-Bit- als auch 64-Bit-Architekturen, mit Python-Versionen von 2.6 bis 3.4" aus Documentation
Cecilia,
2
Warum stimmt diese Zahl nicht mit der im Prozess-Explorer überein? Die Zahl von Psutil scheint immer um etwa 10% größer zu sein.
Worte für
39
Beachten Sie, dass Psutil nicht in der Standardbibliothek ist
Grisaitis
12
Neuere Versionen psutil, psutil.Process()entspricht psutil.Process(os.getpid()). Das ist eine Sache weniger, an die Sie denken müssen, um zu tippen.
Rnorris
208

Für Unix-basierte Systeme (Linux, Mac OS X, Solaris) können Sie die getrusage()Funktion aus dem Standardbibliotheksmodul verwenden resource. Das resultierende Objekt hat das Attribut ru_maxrss, das die maximale Speichernutzung für den aufrufenden Prozess angibt:

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

Die Python-Dokumente notieren die Einheiten nicht. Informationen zum man getrusage.2Wert des Geräts finden Sie auf der Seite Ihres Systems . Unter Ubuntu 18.04 wird die Einheit als Kilobyte angegeben. Unter Mac OS X sind es Bytes.

Die getrusage()Funktion kann auch angegeben werden resource.RUSAGE_CHILDREN, um die Verwendung für untergeordnete Prozesse und (auf einigen Systemen) resource.RUSAGE_BOTHfür die vollständige (selbst und untergeordnete) Prozessnutzung abzurufen.

Wenn Sie sich nur für Linux interessieren, können Sie alternativ die Datei /proc/self/statusoder lesen, /proc/self/statmwie in anderen Antworten auf diese und diese Frage beschrieben .

Nathan Craike
quelle
2
Okay, wird reichen. Ich war mir nicht sicher, ob SO einen Prozess zum Zusammenführen von Fragen hatte oder was. Der doppelte Beitrag sollte teilweise zeigen, dass es für beide Fragen eine Standardbibliothekslösung gibt ... und teilweise für den Repräsentanten. ;) Soll ich diese Antwort löschen?
Nathan Craike
6
Mac OS gibt das RSS definitiv in Bytes zurück, Linux in Kilobyte.
Neil
13
Die Einheiten sind NICHT in Kilobyte. Es ist plattformabhängig, daher müssen Sie resource.getpagesize () verwenden, um dies herauszufinden. Die angegebenen Python-Dokumente ( docs.python.org/2/library/resource.html#resource-usage ) sind tatsächlich sehr klar. Es ist 4096 in meiner Box.
Ben Lin
5
@ BenLin Diese Python-Dokumente sind eindeutig falsch oder es gibt einen Fehler in der Mac-Version. Die von getrusage verwendete Einheit und der von getpagesize zurückgegebene Wert sind definitiv unterschiedlich.
Andrew
6
Die Frage zur aktuellen Verwendung gestellt. Beachten Sie, dass dies die maximale Nutzung ist. (Immer noch eine nützliche Antwort, nur um Leute zu warnen, die sie versehentlich kopiert und eingefügt haben.)
Luc
65

Unter Windows können Sie WMI ( Homepage , Cheeseshop ) verwenden:


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)

Unter Linux (aus dem Python-Kochbuch 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
quelle
14
Der Windows-Code funktioniert bei mir nicht. Diese Änderung hat:return int(result[0].WorkingSet)
John Fouhy
1
Dieser Windows-Code funktioniert unter Windows 7 x64 nicht für mich, selbst nach John Fouhys Kommentaränderung.
Basj
Ich habe diesen Fehler: return [ wmi_object (obj, instance_of, fields) für obj in self._raw_query (wql)] Datei "C: \ Python27 \ lib \ site-packages \ win32com \ client \ util.py", Zeile 84, in der nächsten Rückgabe _get_good_object_ (self._iter .next (), resultCLSID = self.resultCLSID) pywintypes.com_error: (-2147217385, 'OLE-Fehler 0x80041017', None, None) Wenn mir jemand helfen kann? Ich habe 8 x64 gewonnen, aber Python auf x32
Radu Vlad
Hinweis: Ich habe das Windows-Beispiel nach dem Vorschlag von John Fouhy aktualisiert, nachdem ich die (neueste) Quelle des wmi-Moduls überprüft hatte. Siehe auch (1) , (2) .
jedwards
33

Unter Unix können Sie das psTool verwenden, um es zu überwachen:

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

Dabei ist 1347 eine Prozess-ID. Das Ergebnis ist auch in MB.

Bayer
quelle
7

Aktuelle Speichernutzung des aktuellen Prozesses unter Linux für Python 2, Python 3 und Pypy ohne Importe:

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

Getestet unter Linux 4.4 und 4.9, aber auch eine frühe Linux-Version sollte funktionieren.

Beim Suchen man procund Suchen nach Informationen in der /proc/$PID/statusDatei werden Mindestversionen für einige Felder erwähnt (z. B. Linux 2.6.10 für "VmPTE"), aber das Feld "VmRSS" (das ich hier verwende) wird nicht erwähnt. Daher gehe ich davon aus, dass es seit einer frühen Version vorhanden ist.

Luc
quelle
5

Ich mag es , danke für @bayer. Ich bekomme jetzt ein spezielles Tool zur Prozesszählung.

# 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

Fügen Sie meine Prozessliste hinzu.

$ 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

Referenz

Chu-Siang Lai
quelle
Nur eine Optimierung des Codes, um Multi-Pipe zu vermeidenps aux | awk '/python/{sum+=$6}; END {print sum/1024 " MB"}'
NeronLeVelu
4

Für Python 3.6 und psutil 5.4.5 ist es einfacher, memory_percent()die hier aufgeführten Funktionen zu verwenden .

import os
import psutil
process = psutil.Process(os.getpid())
print(process.memory_percent())
A.Ametov
quelle
1
Dies erfordert lib psutil
confiq
4

Noch einfacher zu bedienen als /proc/self/status: /proc/self/statm. Es ist nur eine durch Leerzeichen getrennte Liste mehrerer Statistiken . Ich konnte nicht feststellen, ob beide Dateien immer vorhanden sind.

/ proc / [pid] / statm

Bietet Informationen zur Speichernutzung, gemessen in Seiten. Die Spalten sind:

  • Größe (1) Gesamtprogrammgröße (wie VmSize in / proc / [pid] / status)
  • resident (2) Größe des residenten Sets (wie VmRSS in / proc / [pid] / status)
  • shared (3) Anzahl der residenten freigegebenen Seiten (dh von einer Datei gesichert) (wie RssFile + RssShmem in / proc / [pid] / status)
  • Text (4) Text (Code)
  • Bibliothek lib (5) (seit Linux 2.6 nicht mehr verwendet; immer 0)
  • Daten (6) Daten + Stapel
  • dt (7) Dirty Pages (seit Linux 2.6 nicht mehr verwendet; immer 0)

Hier ist ein einfaches Beispiel:

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)

Das ergibt eine Liste, die ungefähr so ​​aussieht:

0
0
368640
368640
368640
638976
638976
909312
909312
909312

Sie können sehen, dass es nach ungefähr 3 Zuweisungen von 100.000 Bytes um ungefähr 300.000 Bytes springt.

Don Kirkby
quelle
4

Unten ist mein Funktionsdekorator, mit dem Sie verfolgen können, wie viel Speicher dieser Prozess vor dem Funktionsaufruf verbraucht hat, wie viel Speicher er nach dem Funktionsaufruf verwendet und wie lange die Funktion ausgeführt wird.

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

Also, wenn Sie eine Funktion damit dekoriert haben

from utils import track

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

Sie können diese Ausgabe sehen:

inside list create
list_create: memory before: 45,928,448, after: 46,211,072, consumed: 282,624; exec time: 00:00:00
Ihor B.
quelle
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
quelle
7
Dies könnte durch eine Erklärung dessen, was es tut und wie es funktioniert, verbessert werden.
ArtOfWarfare
2
Aufgrund der großen Anzahl zurückgegebener Daten (8 Stellen) und der Tatsache, dass ich nicht viel mache, schätze ich, dass dies Bytes sein müssen. Für eine eher inaktive interaktive Instanz sind es also ungefähr 28,5 MB. (Wow ... ich wusste nicht einmal, dass der obige Kommentar von vor 4 Jahren von mir stammt ... das ist komisch.)
ArtOfWarfare
3

Für Unix-Systeme timegibt Ihnen der Befehl (/ usr / bin / time) diese Informationen, wenn Sie -v übergeben. Siehe Maximum resident set sizeunten, dies ist der maximale ( maximale ) reale (nicht virtuelle) Speicher, der während der Programmausführung verwendet wurde :

$ /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
quelle
1
Beachten Sie, dass dies fehlschlagen kann, wenn Sie nur versuchen, timeanstelle von zu verwenden /usr/bin/time. Siehe: askubuntu.com/questions/434289/…
ungefähr
1

Verwenden Sie sh und os, um in die Antwort von Python Bayer zu gelangen.

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

Die Antwort ist in Megabyte.

Newmu
quelle
4
Es sollte beachtet werden, dass `sh 'kein stdlib-Modul ist. Es kann jedoch mit pip installiert werden.
Jürgen A. Erhard