Rò rỉ bộ nhớ Python [đã đóng]


180

Tôi có một tập lệnh chạy dài, nếu để chạy đủ lâu, sẽ tiêu tốn tất cả bộ nhớ trên hệ thống của tôi.

Không đi sâu vào chi tiết về kịch bản, tôi có hai câu hỏi:

  1. Có bất kỳ "Thực tiễn tốt nhất" nào cần tuân theo, điều này sẽ giúp ngăn chặn rò rỉ xảy ra không?
  2. Có những kỹ thuật nào để gỡ lỗi rò rỉ bộ nhớ trong Python?

5
Tôi đã tìm thấy công thức này hữu ích.
David Schein

Nó dường như in ra quá nhiều dữ liệu có ích
Casebash

1
@Casebash: Nếu chức năng đó in bất cứ điều gì bạn nghiêm túc làm sai. Nó liệt kê các đối tượng với __del__phương thức không còn được tham chiếu ngoại trừ chu kỳ của chúng. Các chu kỳ không thể bị phá vỡ, vì các vấn đề với __del__. Sửa nó!
Helmut Grohne

Câu trả lời:



83

Tôi đã thử hầu hết các tùy chọn được đề cập trước đây nhưng thấy gói nhỏ và trực quan này là tốt nhất: pympler

Nó khá thẳng về phía trước để theo dõi các đối tượng không được thu gom rác, hãy kiểm tra ví dụ nhỏ này:

cài đặt gói qua pip install pympler

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

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

tracker.print_diff()

Đầu ra cho bạn thấy tất cả các đối tượng đã được thêm vào, cộng với bộ nhớ mà chúng tiêu thụ.

Đầu ra mẫu:

                                 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

Gói này cung cấp một số tính năng hơn. Kiểm tra tài liệu của pympler , đặc biệt là phần Xác định rò rỉ bộ nhớ .


5
Điều đáng chú ý là pymplercó thể CHẬM . Nếu bạn đang làm gì đó bán thời gian thực, nó hoàn toàn có thể làm tê liệt hiệu suất ứng dụng của bạn.
Tên giả

@sebpiq thật kỳ lạ, điều tương tự cũng xảy ra với tôi ... bạn có biết tại sao điều này lại xảy ra không? Nhìn nhanh vào mã nguồn không có thông tin chi tiết thực sự.
linusg

25

Hãy để tôi giới thiệu công cụ mem_top tôi đã tạo

Nó giúp tôi giải quyết một vấn đề tương tự

Nó ngay lập tức hiển thị các nghi phạm hàng đầu về rò rỉ bộ nhớ trong chương trình Python


1
điều đó đúng ... nhưng nó mang lại rất ít trong cách sử dụng / giải thích kết quả
me_

@me_, công cụ này có cả phần "Cách sử dụng" và "Giải thích kết quả". Tôi có nên thêm lời giải thích như "refs là số lượng tài liệu tham khảo từ đối tượng, loại là số lượng đối tượng của loại này, byte là kích thước của đối tượng" - sẽ không quá rõ ràng để làm tài liệu này?
Denis Ryzhkov

Các tài liệu sử dụng của công cụ đưa ra một dòng duy nhất "theo thời gian: log.debug (mem_top ())", trong khi giải thích về kết quả của nó là trải nghiệm theo dõi lỗi thực tế của tác giả mà không có ngữ cảnh ... đó không phải là thông số kỹ thuật cho biết một nhà phát triển chính xác những gì họ đang xem ... Tôi không gõ câu trả lời của bạn ... nó cho thấy nghi phạm cấp cao là hóa đơn ... nó không cung cấp tài liệu đầy đủ để hiểu đầy đủ kết quả sử dụng ... ví dụ , trong đầu ra "Giải thích kết quả" tại sao "GearmanJobRequest" rõ ràng là một vấn đề? không có lời giải thích cho lý do tại sao ...
me_

1
Tôi đoán tôi đang vô tình gõ công cụ của bạn, bạn là tác giả ... không có ý định xúc phạm ...
tôi_

6
@me_, tôi vừa thêm bước tiếp theo vào "Cách sử dụng", thêm phần "Bộ đếm", thêm lời giải thích tại sao chính xác Gearman là nghi phạm trong ví dụ thực tế đó, ghi lại từng tham số tùy chọn của "mem_top ()" trong mã, và đã tải lên tất cả điều này dưới dạng v0.1.7 - vui lòng xem nếu có gì khác có thể được cải thiện. Cảm ơn bạn! )
Denis Ryzhkov

18

Mô-đun Tracemalloc được tích hợp dưới dạng mô-đun tích hợp bắt đầu từ Python 3.4 và dường như, nó cũng có sẵn cho các phiên bản trước của Python dưới dạng thư viện của bên thứ ba (mặc dù chưa thử nghiệm).

Mô-đun này có thể xuất các tệp và dòng chính xác được phân bổ nhiều bộ nhớ nhất. IMHO, thông tin này cực kỳ có giá trị hơn số lượng phiên bản được phân bổ cho từng loại (kết quả là rất nhiều bộ dữ liệu 99% thời gian, là một đầu mối, nhưng hầu như không giúp ích trong hầu hết các trường hợp).

Tôi khuyên bạn nên sử dụng tracemalloc kết hợp với pyrasite . 9 trong số 10 lần, chạy 10 đoạn trích hàng đầu trong vỏ pyrasite sẽ cung cấp cho bạn đủ thông tin và gợi ý để khắc phục rò rỉ trong vòng 10 phút. Tuy nhiên, nếu bạn vẫn không thể tìm thấy nguyên nhân rò rỉ, vỏ pyrasite kết hợp với các công cụ khác được đề cập trong chủ đề này có thể cũng sẽ cung cấp cho bạn một số gợi ý nữa. Bạn cũng nên xem tất cả các trợ giúp bổ sung được cung cấp bởi pyrasite (chẳng hạn như trình xem bộ nhớ).


pytracemalloc.readthedocs.io không còn tồn tại nữa
Dimitrios Mistriotis

12

Bạn nên đặc biệt có một cái nhìn về dữ liệu toàn cầu hoặc tĩnh (dữ liệu sống lâu).

Khi dữ liệu này phát triển mà không bị hạn chế, bạn cũng có thể gặp rắc rối trong Python.

Trình thu gom rác chỉ có thể thu thập dữ liệu, không được tham chiếu nữa. Nhưng dữ liệu tĩnh của bạn có thể kết nối các yếu tố dữ liệu cần được giải phóng.

Một vấn đề khác có thể là chu kỳ bộ nhớ, nhưng ít nhất về lý thuyết, người thu gom Rác nên tìm và loại bỏ các chu kỳ - ít nhất là miễn là chúng không bị mắc vào một số dữ liệu sống lâu.

Những loại dữ liệu sống lâu là đặc biệt rắc rối? Có một cái nhìn tốt về bất kỳ danh sách và từ điển - chúng có thể phát triển mà không có giới hạn. Trong từ điển, bạn thậm chí có thể không thấy sự cố xảy ra kể từ khi bạn truy cập từ điển, số lượng khóa trong từ điển có thể không hiển thị lớn đối với bạn ...


7

Để phát hiện và xác định vị trí rò rỉ bộ nhớ cho các quy trình chạy dài, ví dụ như trong môi trường sản xuất, giờ đây bạn có thể sử dụng stackimpact . Nó sử dụng tracemalloc bên dưới. Thêm thông tin trong bài này .

nhập mô tả hình ảnh ở đây


4

Theo như thực hành tốt nhất, hãy để mắt đến các chức năng đệ quy. Trong trường hợp của tôi, tôi gặp vấn đề với đệ quy (nơi không cần phải có). Một ví dụ đơn giản về những gì tôi đã làm:

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

Hoạt động theo cách đệ quy này sẽ không kích hoạt bộ sưu tập rác và dọn sạch phần còn lại của chức năng, do đó, mỗi lần sử dụng bộ nhớ đều tăng và tăng.

Giải pháp của tôi là rút cuộc gọi đệ quy ra khỏi my_feft () và xử lý hàm main () khi gọi lại. bằng cách này, chức năng kết thúc một cách tự nhiên và làm sạch sau đó.

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
Sử dụng đệ quy theo cách này cũng sẽ bị hỏng nếu bạn đạt giới hạn độ sâu đệ quy vì Python không tối ưu hóa các cuộc gọi đuôi. Theo mặc định, đây là 1000 cuộc gọi đệ quy.
Lie Ryan

3

Không chắc chắn về "Thực tiễn tốt nhất" đối với rò rỉ bộ nhớ trong python, nhưng python nên xóa bộ nhớ của chính nó bởi trình thu gom rác. Vì vậy, chủ yếu tôi sẽ bắt đầu bằng cách kiểm tra danh sách vòng tròn của một số ngắn, vì chúng sẽ không được người thu gom rác nhặt được.


3
hoặc tham chiếu đến các đối tượng đang được lưu giữ mãi mãi, v.v.
matt b

3
Các bạn có thể vui lòng cung cấp các ví dụ về danh sách tròn và các đối tượng đang được lưu giữ mãi mãi không?
Daniel

2

Đây không phải là lời khuyên đầy đủ. Nhưng điều số một cần lưu ý khi viết với suy nghĩ tránh rò rỉ bộ nhớ trong tương lai là đảm bảo rằng bất cứ điều gì chấp nhận tham chiếu đến cuộc gọi lại, nên lưu trữ cuộc gọi lại đó như một tài liệu tham khảo yếu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.