หน่วยความจำหลามรั่ว [ปิด]


180

ฉันมีสคริปต์ที่ใช้เวลานานซึ่งหากปล่อยให้ทำงานนานพอจะใช้หน่วยความจำทั้งหมดในระบบของฉัน

ฉันไม่มีคำถามสองข้อ:

  1. มี "วิธีปฏิบัติที่ดีที่สุด" ที่จะปฏิบัติตามซึ่งจะช่วยป้องกันการรั่วไหลที่เกิดขึ้น?
  2. มีเทคนิคอะไรบ้างในการดีบักการรั่วไหลของหน่วยความจำใน Python

5
ฉันพบว่าสูตรนี้มีประโยชน์
David Schein

มันดูเหมือนว่าจะพิมพ์ออกมาทางข้อมูลมากเกินไปที่จะเป็นประโยชน์
Casebash

1
@Casebash: หากฟังก์ชันนั้นพิมพ์สิ่งใดก็ตามที่คุณทำผิดอย่างจริงจัง มันแสดงรายการวัตถุด้วย__del__วิธีที่ไม่ได้อ้างอิงอีกต่อไปยกเว้นวงจรของพวกเขา __del__วงจรไม่สามารถหักเนื่องจากปัญหาเกี่ยวกับ ซ่อมมัน!
Helmut Grohne

คำตอบ:


106

ดูที่บทความนี้: การติดตามหน่วยความจำหลามรั่ว

นอกจากนี้โปรดทราบว่าโมดูลรวบรวมขยะสามารถตั้งค่าสถานะการดีบักได้จริง ดูset_debugฟังก์ชั่น นอกจากนี้ดูที่รหัสนี้โดย Gnibblerเพื่อกำหนดประเภทของวัตถุที่สร้างขึ้นหลังจากการโทร


83

ฉันลองใช้ตัวเลือกส่วนใหญ่ที่กล่าวถึงก่อนหน้านี้ แต่พบว่าแพคเกจขนาดเล็กและใช้งานง่ายนี้จะดีที่สุด: pympler

ค่อนข้างตรงไปตรงมาเพื่อติดตามวัตถุที่ไม่ได้เก็บขยะตรวจสอบตัวอย่างเล็ก ๆ นี้:

ติดตั้งแพ็คเกจผ่าน pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

เอาท์พุทแสดงให้คุณเห็นวัตถุทั้งหมดที่ได้รับการเพิ่มรวมทั้งหน่วยความจำที่พวกเขาใช้

ตัวอย่างผลลัพธ์:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

แพ็คเกจนี้มีคุณสมบัติเพิ่มเติมมากมาย ตรวจสอบเอกสารของ pymplerโดยเฉพาะในส่วนการระบุการรั่วไหลของหน่วยความจำ


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

@sebpiq แปลก ๆ เกิดขึ้นกับฉัน ... คุณมีความคิดว่าทำไมสิ่งนี้ถึงเกิดขึ้น? การดูอย่างรวดเร็วของซอร์สโค้ดนั้นไม่ทำให้เกิดความเข้าใจที่แท้จริง
linusg

25

ให้ฉันแนะนำเครื่องมือmem_topที่ฉันสร้างขึ้น

มันช่วยให้ฉันแก้ปัญหาที่คล้ายกัน

เพียงแสดงผู้ต้องสงสัยอันดับต้น ๆ สำหรับหน่วยความจำรั่วในโปรแกรม Python ทันที


1
มันเป็นความจริง ... แต่มันให้วิธีการใช้งาน / คำอธิบายผลลัพธ์น้อยมาก
me_

@me_ เครื่องมือนี้มีทั้งส่วน "การใช้งาน" และ "การอธิบายผลลัพธ์" ฉันควรเพิ่มคำอธิบายเช่น "refs คือจำนวนการอ้างอิงจากวัตถุชนิดคือจำนวนวัตถุชนิดนี้ไบต์มีขนาดของวัตถุ" - มันจะไม่ชัดเจนเกินไปที่จะบันทึกเอกสารนี้
เดนิส Ryzhkov

เอกสารการใช้งานของเครื่องมือให้บรรทัดเดียวว่า "เป็นครั้งคราว: logging.debug (mem_top ())" ในขณะที่คำอธิบายของผลลัพธ์คือประสบการณ์การติดตามข้อผิดพลาดในชีวิตจริงของผู้เขียนโดยไม่มีบริบท ... นั่นไม่ใช่ข้อกำหนดทางเทคนิคที่บอก สิ่งที่พวกเขากำลังมองหา ... ฉันไม่ได้เคาะคำตอบของคุณ ... มันแสดงให้เห็นถึงผู้ต้องสงสัยระดับสูงที่ถูกเรียกเก็บเงิน ... มันไม่ได้ให้เอกสารเพียงพอที่จะเข้าใจผลการใช้อย่างสมบูรณ์ ... เช่น ในผลลัพธ์ "การอธิบายผลลัพธ์" เหตุใด "GearmanJobRequest" จึงเห็นได้ชัดว่าเป็นปัญหา ไม่มีคำอธิบายว่าทำไม ...
me_

1
ฉันเดาว่าฉันกำลังเคาะเครื่องมือของคุณโดยไม่ได้ตั้งใจคุณเป็นผู้เขียน ... ไม่มีความผิดใด ๆ ที่ตั้งใจ ...
me_

6
@ me_ ฉันเพิ่งเพิ่มขั้นตอนถัดไปในส่วน "การใช้งาน" เพิ่ม "ตัวนับ" เพิ่มคำอธิบายว่าทำไม Gearman จึงเป็นผู้ต้องสงสัยในชีวิตจริงตัวอย่างนั้นบันทึกเอกสารตัวเลือกพารามิเตอร์ "mem_top ()" ในรหัส และอัปโหลดทั้งหมดนี้เป็น v0.1.7 - โปรดดูว่ามีอะไรที่ปรับปรุงได้บ้าง ขอบคุณ! )
Denis Ryzhkov

18

โมดูล Tracemallocได้รับการรวมเป็นโมดูลในตัวโดยเริ่มต้นจาก Python 3.4 และดูเหมือนว่าจะมีให้สำหรับ Python เวอร์ชันก่อนหน้าในฐานะห้องสมุดบุคคลที่สาม (ยังไม่ได้ทดสอบ)

โมดูลนี้สามารถส่งออกไฟล์และบรรทัดที่แม่นยำซึ่งจัดสรรหน่วยความจำได้มากที่สุด IMHO ข้อมูลนี้มีค่ามากกว่าจำนวนอินสแตนซ์ที่จัดสรรสำหรับแต่ละประเภท (ซึ่งจบลงด้วยการเพิ่มจำนวนมาก 99% ของเวลาซึ่งเป็นเบาะแส แต่แทบจะไม่ช่วยในกรณีส่วนใหญ่)

ผมขอแนะนำให้คุณใช้ tracemalloc ร่วมกับpyrasite 9 ครั้งใน 10 ครั้งการเรียกใช้ข้อมูลตัวอย่าง 10 อันดับแรกในเปลือกหอยจะช่วยให้คุณได้รับข้อมูลและคำแนะนำในการแก้ไขปัญหาการรั่วไหลภายใน 10 นาที แต่ถ้าคุณยังไม่สามารถหาสาเหตุการรั่วได้ Pyrasite-shell ร่วมกับเครื่องมืออื่น ๆ ที่กล่าวถึงในหัวข้อนี้อาจจะให้คำแนะนำเพิ่มเติมแก่คุณเช่นกัน คุณควรดูผู้ช่วยเหลือพิเศษทั้งหมดที่มีให้โดย pyrasite (เช่นโปรแกรมดูหน่วยความจำ)


pytracemalloc.readthedocs.ioไม่มีอยู่อีกต่อไป
Dimitrios Mistriotis

12

คุณควรดูข้อมูลโกลบอลหรือสแตติกของคุณเป็นพิเศษ (ข้อมูลอายุยืน)

เมื่อข้อมูลนี้เติบโตโดยไม่มีข้อ จำกัด คุณสามารถพบปัญหาใน Python

ตัวรวบรวมขยะสามารถรวบรวมข้อมูลได้เท่านั้นซึ่งไม่ได้อ้างอิงอีกต่อไป แต่ข้อมูลคงที่ของคุณสามารถเชื่อมต่อองค์ประกอบข้อมูลที่ควรจะเป็นอิสระ

ปัญหาอื่นอาจเป็นวงจรหน่วยความจำ แต่อย่างน้อยในทางทฤษฎีแล้วนักสะสมขยะควรค้นหาและกำจัดรอบ - อย่างน้อยตราบใดที่พวกเขาไม่ได้ติดอยู่กับข้อมูลที่มีชีวิตที่ยาวนาน

ข้อมูลอายุยืนชนิดใดที่มีปัญหาเป็นพิเศษ? ดูดีในรายการและพจนานุกรม - สามารถเติบโตได้โดยไม่ จำกัด ในพจนานุกรมคุณอาจไม่เห็นปัญหามาตั้งแต่เมื่อคุณเข้าถึง dicts จำนวนของคีย์ในพจนานุกรมอาจไม่สามารถมองเห็นคุณได้อย่างชัดเจน ...


7

ในการตรวจสอบและค้นหาการรั่วไหลของหน่วยความจำสำหรับกระบวนการทำงานนานเช่นในสภาพแวดล้อมการผลิตตอนนี้คุณสามารถใช้stackimpact มันใช้tracemallocใต้ ข้อมูลเพิ่มเติมในโพสต์นี้

ป้อนคำอธิบายรูปภาพที่นี่


4

สำหรับแนวปฏิบัติที่ดีที่สุดให้จับตาดูฟังก์ชั่นวนซ้ำ ในกรณีของฉันฉันพบปัญหาในการเรียกซ้ำ (ซึ่งไม่จำเป็นต้องมี) ตัวอย่างที่เรียบง่ายของสิ่งที่ฉันทำ:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

การดำเนินการในลักษณะวนซ้ำนี้จะไม่ทำให้เกิดการเก็บรวบรวมขยะและกำจัดส่วนที่เหลือของฟังก์ชั่นดังนั้นทุกครั้งที่มีการใช้งานหน่วยความจำเพิ่มขึ้นเรื่อย ๆ

โซลูชันของฉันคือดึงการเรียกซ้ำออกจาก my_function () และมีตัวจัดการ main () เมื่อโทรอีกครั้ง วิธีนี้จะสิ้นสุดการทำงานตามธรรมชาติและทำความสะอาดหลังจากนั้น

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

7
การใช้การเรียกซ้ำในลักษณะนี้จะแตกถ้าคุณกดขีดจำกัดความลึกของการเรียกซ้ำเนื่องจาก Python ไม่ปรับการเรียกหางให้เหมาะสม นี่คือการโทรซ้ำ 1,000 ครั้ง
Lie Ryan

3

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


3
หรือการอ้างอิงไปยังวัตถุที่ถูกเก็บไว้ตลอดไป ฯลฯ
matt b

3
พวกคุณช่วยยกตัวอย่างของรายการวงกลมและวัตถุที่ถูกเก็บไว้ตลอดไปได้ไหม
Daniel

2

นี่คือคำแนะนำที่ละเอียดถี่ถ้วน แต่สิ่งหนึ่งที่ควรคำนึงถึงเมื่อเขียนด้วยความคิดที่จะหลีกเลี่ยงการรั่วไหลของหน่วยความจำในอนาคต (ลูป) คือการตรวจสอบให้แน่ใจว่าสิ่งใดก็ตามที่ยอมรับการอ้างอิงถึงการโทรกลับควรเก็บการติดต่อนั้นไว้เป็นการอ้างอิงที่อ่อนแอ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.