หน่วยความจำทั้งหมดที่ใช้โดยกระบวนการ Python?


266

มีวิธีใดบ้างที่โปรแกรม Python จะพิจารณาจำนวนหน่วยความจำที่ใช้อยู่ในปัจจุบัน? ฉันเคยเห็นการสนทนาเกี่ยวกับการใช้หน่วยความจำสำหรับวัตถุเดียว แต่สิ่งที่ฉันต้องการคือการใช้หน่วยความจำทั้งหมดสำหรับกระบวนการเพื่อให้ฉันสามารถกำหนดได้ว่าเมื่อใดที่จำเป็นต้องเริ่มทิ้งข้อมูลแคช

คำตอบ:


303

นี่คือโซลูชันที่มีประโยชน์ที่ใช้งานได้กับระบบปฏิบัติการต่าง ๆ รวมถึง Linux, Windows 7 และอื่น ๆ :

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

ใน Python 2.7 ปัจจุบันของฉันติดตั้งด้วย psutil 5.6.3 บรรทัดสุดท้ายควรเป็น

print(process.memory_info()[0])

แทน (มีการเปลี่ยนแปลงใน API)

หมายเหตุ: pip install psutilถ้ายังไม่ได้ติดตั้ง


3
psutilเป็นแพลตฟอร์มข้ามและสามารถส่งคืนค่าเดียวกับpsเครื่องมือบรรทัดคำสั่ง: pythonhosted.org/psutil/#psutil.Process.memory_info
amos

1
"( psutil) ปัจจุบันรองรับ Linux, Windows, OSX, FreeBSD และ Sun Solaris ทั้งสถาปัตยกรรมแบบ 32 บิตและ 64 บิตโดยมีเวอร์ชัน Python จาก 2.6 เป็น 3.4" จากเอกสาร
Cecilia

2
เหตุใดตัวเลขนี้จึงไม่ตรงกับตัวเลขในตัวสำรวจกระบวนการ ดูเหมือนว่าจำนวน psutil จะใหญ่ขึ้นประมาณ 10%
Wordsforthewise

39
โปรดทราบว่า psutil ไม่ได้อยู่ในห้องสมุดมาตรฐาน
grisaitis

12
สำหรับรุ่นล่าสุดpsutil, เทียบเท่ากับpsutil.Process() psutil.Process(os.getpid())นั่นเป็นสิ่งหนึ่งที่คุณต้องจำให้พิมพ์น้อยลง
rnorris

208

สำหรับระบบปฏิบัติการ Unix based (Linux, Mac OS X, Solaris), คุณสามารถใช้ฟังก์ชั่นจากโมดูลห้องสมุดมาตรฐานgetrusage() resourceวัตถุผลลัพธ์มีแอตทริบิวต์ru_maxrssซึ่งให้การใช้หน่วยความจำสูงสุดสำหรับกระบวนการเรียก:

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

เอกสารหลามไม่ได้จดบันทึกของหน่วย อ้างถึงman getrusage.2หน้าของระบบเฉพาะของคุณเพื่อตรวจสอบหน่วยสำหรับค่า บน Ubuntu 18.04 ยูนิตถูกบันทึกเป็นกิโลไบต์ บน Mac OS X เป็นไบต์

getrusage()ฟังก์ชั่นนอกจากนี้ยังจะได้รับresource.RUSAGE_CHILDRENที่จะได้รับการใช้งานสำหรับกระบวนการที่เด็กและ (ในบางระบบ) resource.RUSAGE_BOTHรวมทั้งสิ้น (ตัวเองและเด็ก) ใช้กระบวนการ

หากคุณสนใจเฉพาะ Linux คุณสามารถอ่าน/proc/self/statusหรือ/proc/self/statmไฟล์ตามที่อธิบายไว้ในคำตอบอื่น ๆ สำหรับคำถามนี้และคำถามนี้ได้เช่นกัน


2
โอเคจะทำ ฉันไม่แน่ใจว่าถ้าเช่นนั้นมีกระบวนการในการรวมคำถามหรืออะไร โพสต์ที่ซ้ำกันส่วนหนึ่งเพื่อแสดงให้ผู้คนเห็นว่ามีวิธีแก้ปัญหาห้องสมุดมาตรฐานทั้งคำถาม ... และอีกส่วนสำหรับตัวแทน ;) ฉันควรลบคำตอบนี้หรือไม่
Nathan Craike

6
Mac OS ส่งคืน RSS เป็นไบต์อย่างแน่นอน Linux ส่งกลับเป็นกิโลไบต์
Neil

13
หน่วยไม่ได้เป็นกิโลไบต์ มันขึ้นอยู่กับแพลตฟอร์มดังนั้นคุณต้องใช้ resource.getpagesize () เพื่อค้นหา Python docs ที่ให้มา ( docs.python.org/2/library/resource.html#resource-usage ) มีความชัดเจนเกี่ยวกับเรื่องนี้ มันคือ 4096 ในกล่องของฉัน
เบ็นหลิน

5
@BenLin เอกสาร Python เหล่านั้นผิดอย่างชัดเจนหรือมีข้อผิดพลาดในเวอร์ชัน Mac หน่วยที่ใช้โดย getrusage และค่าที่ส่งคืนโดย getpagesize นั้นแตกต่างกันอย่างแน่นอน
Andrew

6
คำถามที่ถามถึงการใช้งานในปัจจุบัน โปรดทราบว่านี่เป็นการใช้งานสูงสุด (ยังเป็นคำตอบที่มีประโยชน์เพียงเตือนผู้คนที่คัดลอกมาวางโดยไม่ตั้งใจ)
Luc

65

บน Windows คุณสามารถใช้ WMI ( โฮมเพ , 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)

บน Linux (จาก python cookbook 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
รหัส Windows ใช้ไม่ได้สำหรับฉัน การเปลี่ยนแปลงนี้จะ:return int(result[0].WorkingSet)
John Fouhy

1
รหัส Windows นี้ใช้งานไม่ได้สำหรับฉันใน Windows 7 x64 แม้หลังจากการปรับเปลี่ยนความคิดเห็นของ John Fouhy
Basj

ฉันมีข้อผิดพลาดนี้: ส่งคืน [ wmi_object (obj, instance_of, ฟิลด์) สำหรับ obj ด้วยตนเอง _raw_query (wql)] ไฟล์ "C: \ Python27 \ lib \ site-packages \ win32com \ client \ util.py", บรรทัด 84, ในการกลับมาครั้งต่อไป _get_good_object_ (self._iter . ถัดไป (), resultCLSID = self.resultCLSID) pywintypes.com_error: (-2147217385, 'ข้อผิดพลาด OLE 0x80041017', ไม่มี, ไม่มี) ถ้าใครสามารถช่วยฉันได้? ฉันชนะ 8 x64 แต่ python ใน x32
Radu Vlad

หมายเหตุ: ฉันได้อัปเดตตัวอย่างหน้าต่างตามคำแนะนำของ John Fouhy หลังจากตรวจสอบแหล่งที่มา (ล่าสุด) ของโมดูล wmi ดูเพิ่มเติม(1) , (2)
jedwards

33

บนยูนิกซ์คุณสามารถใช้psเครื่องมือเพื่อตรวจสอบ:

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

โดยที่ 1347 คือ id กระบวนการบางส่วน นอกจากนี้ผลลัพธ์ยังมีหน่วยเป็น MB


8

การใช้หน่วยความจำปัจจุบันของกระบวนการปัจจุบันบน Linuxสำหรับ Python 2 , Python 3และpypyโดยไม่ต้องอิมพอร์ต:

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

มันอ่านไฟล์สถานะของกระบวนการปัจจุบันใช้ทุกอย่างหลังจากVmRSS:นั้นนำทุกอย่างก่อนขึ้นบรรทัดใหม่ครั้งแรก (แยกค่า VmRSS) และสุดท้ายตัด 3 ไบต์สุดท้ายซึ่งเป็นช่องว่างและหน่วย (kB)
ในการส่งคืนจะตัดช่องว่างและส่งกลับเป็นหมายเลข

ทดสอบบน Linux 4.4 และ 4.9 แต่ถึงแม้ Linux เวอร์ชันก่อนหน้าจะสามารถใช้งานได้: man procการค้นหาและค้นหาข้อมูลใน/proc/$PID/statusไฟล์จะกล่าวถึงเวอร์ชันขั้นต่ำสำหรับบางฟิลด์ (เช่น Linux 2.6.10 สำหรับ "VmPTE") แต่ "VmRSS" "ฟิลด์ (ซึ่งฉันใช้ที่นี่) ไม่มีการกล่าวถึงดังกล่าว ดังนั้นฉันคิดว่ามันมีอยู่ในนั้นตั้งแต่รุ่นแรก


5

ฉันชอบมันขอบคุณสำหรับ @bayer ฉันได้รับเครื่องมือนับกระบวนการเฉพาะตอนนี้

# 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

แนบรายการกระบวนการของฉัน

$ 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

การอ้างอิง


เพียงการเพิ่มประสิทธิภาพของรหัสเพื่อหลีกเลี่ยงท่อหลาย ๆps aux | awk '/python/{sum+=$6}; END {print sum/1024 " MB"}'
NeronLeVelu

4

สำหรับงูหลาม 3.6 และ psutil 5.4.5 มันจะง่ายต่อการใช้งานmemory_percent()ฟังก์ชั่นที่ระบุไว้ที่นี่

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

1
สิ่งนี้ต้องใช้ lib psutil
confiq

4

แม้จะใช้งานง่ายกว่า:/proc/self/status /proc/self/statmมันเป็นเพียงรายการคั่นด้วยช่องว่างของสถิติต่างๆ ฉันไม่สามารถบอกได้ว่ามีไฟล์ทั้งสองอยู่ด้วยหรือไม่

/ proc / [pid] / statm

ให้ข้อมูลเกี่ยวกับการใช้หน่วยความจำซึ่งวัดในหน้า คอลัมน์คือ:

  • ขนาด (1) ขนาดโปรแกรมทั้งหมด (เหมือนกับ VmSize in / proc / [pid] / status)
  • ถิ่นที่อยู่ (2) ขนาดชุดที่อยู่อาศัย (เช่นเดียวกับ VmRSS ใน / proc / [pid] / สถานะ)
  • แบ่งใช้ (3) จำนวนหน้าที่แชร์ถิ่นที่อยู่ (เช่นสำรองข้อมูลโดยไฟล์) (เหมือนกับ RssFile + RssShmem ใน / proc / [pid] / สถานะ)
  • ข้อความ (4) ข้อความ (รหัส)
  • ไลบรารี lib (5) (ไม่ได้ใช้ตั้งแต่ Linux 2.6; 0 เสมอ)
  • data (6) data + stack
  • dt (7) หน้าสกปรก (ไม่ได้ใช้ตั้งแต่ Linux 2.6; เสมอ 0)

นี่คือตัวอย่างง่ายๆ:

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)

ที่สร้างรายการที่มีลักษณะดังนี้:

0
0
368640
368640
368640
638976
638976
909312
909312
909312

คุณจะเห็นว่ามันกระโดดประมาณ 300,000 ไบต์หลังจากการจัดสรร 3 ครั้งโดยประมาณ 100,000 ไบต์


4

ด้านล่างเป็นตัวตกแต่งฟังก์ชั่นของฉันที่อนุญาตให้ติดตามจำนวนหน่วยความจำที่กระบวนการนี้ใช้ก่อนการเรียกใช้ฟังก์ชันจำนวนหน่วยความจำที่ใช้หลังจากการเรียกใช้ฟังก์ชันและระยะเวลาการใช้งานฟังก์ชัน

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

ดังนั้นเมื่อคุณมีฟังก์ชั่นตกแต่งบางอย่างแล้ว

from utils import track

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

คุณจะสามารถเห็นผลลัพธ์นี้:

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
สิ่งนี้สามารถปรับปรุงได้ด้วยคำอธิบายว่ามันทำงานอย่างไรและทำงานอย่างไร
ArtOfWarfare

2
จากจำนวนที่ส่งคืน (8 หลัก) และวิธีที่ฉันไม่ได้ทำอะไรมากฉันคิดว่านี่ต้องเป็นไบต์? ดังนั้นจะอยู่ที่ประมาณ 28.5 MB สำหรับอินสแตนซ์แบบอินเทอร์แอคทีฟ (ว้าว ... ฉันไม่ได้ตระหนักถึงความคิดเห็นข้างต้นเป็นของฉันจาก 4 ปีที่ผ่านมา ... ที่แปลก.)
ArtOfWarfare

3

สำหรับคำสั่งระบบ Unix time(/ usr / bin / time) จะให้ข้อมูลนั้นแก่คุณหากคุณผ่าน -v ดูMaximum resident set sizeด้านล่างซึ่งเป็นหน่วยความจำสูงสุด (สูงสุด) จริง (ไม่ใช่เสมือน) ที่ใช้ระหว่างการทำงานของโปรแกรม :

$ /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
ทราบว่านี้อาจล้มเหลวหากคุณเพียงแค่พยายามที่จะใช้แทนtime /usr/bin/timeดู: askubuntu.com/questions/434289/…
abought

1

ใช้ sh และ os เพื่อรับคำตอบของ python bayer

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

คำตอบมีหน่วยเป็นเมกะไบต์


4
ควรสังเกตว่า `sh 'ไม่ใช่โมดูล stdlib แม้ว่าจะสามารถติดตั้งกับ pip ได้
Jürgen A. Erhard
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.