2009-08-27 32 views
24

Tôi có một tập lệnh đa luồng nhỏ chạy trong django và theo thời gian bắt đầu sử dụng bộ nhớ ngày càng nhiều. Để nó cả ngày ăn khoảng 6GB RAM và tôi bắt đầu trao đổi.Python: Gỡ lỗi rò bộ nhớ

Sau http://www.lshift.net/blog/2008/11/14/tracing-python-memory-leaks Tôi coi đây là loại phổ biến nhất (với chỉ 800M bộ nhớ được sử dụng):

(Pdb) objgraph.show_most_common_types(limit=20) 
dict      43065 
tuple      28274 
function     7335 
list      6157 
NavigableString   3479 
instance     2454 
cell      1256 
weakref     974 
wrapper_descriptor   836 
builtin_function_or_method 766 
type      742 
getset_descriptor   562 
module      423 
method_descriptor   373 
classobj     256 
instancemethod    255 
member_descriptor   218 
property     185 
Comment     183 
__proxy__     155 

mà không hiển thị bất cứ điều gì lạ. Tôi nên làm gì bây giờ để giúp gỡ lỗi các vấn đề bộ nhớ?

Cập nhật: Thử một số thứ mà mọi người đang đề xuất. Tôi chạy chương trình qua đêm, và khi tôi làm việc, 50% * 8G == 4G RAM được sử dụng.

(Pdb) from pympler import muppy 
(Pdb) muppy.print_summary() 
            types | # objects | total size 
========================================== | =========== | ============ 
            unicode |  210997 |  97.64 MB 
             list |  1547 |  88.29 MB 
             dict |  41630 |  13.21 MB 
             set |   50 |  8.02 MB 
             str |  109360 |  7.11 MB 
            tuple |  27898 |  2.29 MB 
             code |  6907 |  1.16 MB 
             type |   760 | 653.12 KB 
            weakref |  1014 |  87.14 KB 
             int |  3552 |  83.25 KB 
        function (__wrapper__) |   702 |  82.27 KB 
         wrapper_descriptor |   998 |  77.97 KB 
             cell |  1357 |  74.21 KB 
    <class 'pympler.asizeof.asizeof._Claskey |  1113 |  69.56 KB 
         function (__init__) |   574 |  67.27 KB 

Điều đó không tính đến 4G và cũng không cung cấp cho tôi bất kỳ dữ liệu lớn nào được cấu trúc để khắc phục. Các unicode là từ một tập hợp() của "thực hiện" các nút, và danh sách của trông giống như chỉ ngẫu nhiên weakref s.

Tôi đã không sử dụng guppy vì nó yêu cầu phần mở rộng C và tôi không có gốc nên sẽ rất khó xây dựng.

Không có đối tượng nào tôi đang sử dụng có phương thức __del__ và nhìn qua các thư viện, nó không giống như django hay python-mysqldb. Bất kỳ ý tưởng nào khác?

+0

"đang chạy ở Django"? Bạn có nghĩa là bạn đang sử dụng máy chủ web Django để thực hiện xử lý nền không dịch vụ web bổ sung? Bạn có cân nhắc việc chia nhỏ nội dung không phân phát web này thành một quy trình riêng biệt không? –

+2

Đó là một công việc cron nhập khẩu settango.py Django và sử dụng nhiều tính năng ORM Django. Vì vậy, nó không phải là sinh ra bởi một máy chủ web, nhưng vẫn sử dụng nhiều tính năng (có thể đã được thích hợp) –

Trả lời

31

Xem http://opensourcehacker.com/2008/03/07/debugging-django-memory-leak-with-trackrefs-and-guppy/. Câu trả lời ngắn gọn: nếu bạn đang chạy django nhưng không theo định dạng dựa trên yêu cầu trên web, bạn cần phải chạy theo cách thủ công db.reset_queries() (và dĩ nhiên có DEBUG = False, như những người khác đã đề cập). Django tự động thực hiện reset_queries() sau một yêu cầu web, nhưng ở định dạng của bạn, điều đó không bao giờ xảy ra.

+2

db.reset_queries() đã giải quyết được vấn đề cho tôi, cảm ơn bạn rất nhiều. – pyeleven

0

Hãy thử Guppy.

Cơ bản, bạn cần thêm thông tin hoặc có thể trích xuất một số thông tin. Guppy thậm chí cung cấp đại diện đồ họa của dữ liệu.

1

Tôi nghĩ bạn nên sử dụng các công cụ khác nhau. Rõ ràng, thống kê bạn nhận được chỉ là về các đối tượng GC (tức là các đối tượng có thể tham gia vào các chu kỳ); đáng chú ý nhất, nó thiếu dây.

Tôi khuyên bạn nên sử dụng Pympler; điều này sẽ cung cấp cho bạn số liệu thống kê chi tiết hơn.

+0

đầu hiển thị ứng dụng của tôi bằng cách sử dụng 7% * 8GB = 560M. pympler.muppy.print_summary() hiển thị khoảng 55M. Phần còn lại ở đâu? –

6

Bạn đã thử gc.set_debug() chưa?

Bạn cần phải tự hỏi mình câu hỏi đơn giản:

  • Tôi có sử dụng đối tượng với __del__ phương pháp? Tôi có hoàn toàn, dứt khoát, cần chúng không?
  • Tôi có thể nhận chu trình tham chiếu trong mã của mình không? Chúng ta không thể phá vỡ những vòng tròn này trước khi loại bỏ các đối tượng?

Xem, vấn đề chính sẽ là một chu kỳ của các đối tượng có chứa __del__ phương pháp:

import gc 

class A(object): 
    def __del__(self): 
     print 'a deleted' 
     if hasattr(self, 'b'): 
      delattr(self, 'b') 

class B(object): 
    def __init__(self, a): 
     self.a = a 
    def __del__(self): 
     print 'b deleted' 
     del self.a 


def createcycle(): 
    a = A() 
    b = B(a) 
    a.b = b 
    return a, b 

gc.set_debug(gc.DEBUG_LEAK) 

a, b = createcycle() 

# remove references 
del a, b 

# prints: 
## gc: uncollectable <A 0x...> 
## gc: uncollectable <B 0x...> 
## gc: uncollectable <dict 0x...> 
## gc: uncollectable <dict 0x...> 
gc.collect() 

# to solve this we break explicitely the cycles: 
a, b = createcycle() 
del a.b 

del a, b 

# objects are removed correctly: 
## a deleted 
## b deleted 
gc.collect() 

tôi thực sự sẽ khuyến khích bạn tới các đối tượng cờ/khái niệm được đi xe đạp trong ứng dụng của bạn và tập trung vào cuộc đời của họ: khi bạn không cần chúng nữa, chúng ta có tham chiếu gì không?

Ngay cả đối với chu kỳ mà không __del__ phương pháp, chúng ta có thể có một vấn đề:

import gc 

# class without destructor 
class A(object): pass 

def createcycle(): 
    # a -> b -> c 
    #^  | 
    # ^<--<--<--| 
    a = A() 
    b = A() 
    a.next = b 
    c = A() 
    b.next = c 
    c.next = a 
    return a, b, b 

gc.set_debug(gc.DEBUG_LEAK) 

a, b, c = createcycle() 
# since we have no __del__ methods, gc is able to collect the cycle: 

del a, b, c 
# no panic message, everything is collectable: 
##gc: collectable <A 0x...> 
##gc: collectable <A 0x...> 
##gc: collectable <dict 0x...> 
##gc: collectable <A 0x...> 
##gc: collectable <dict 0x...> 
##gc: collectable <dict 0x...> 
gc.collect() 

a, b, c = createcycle() 

# but as long as we keep an exterior ref to the cycle...: 
seen = dict() 
seen[a] = True 

# delete the cycle 
del a, b, c 
# nothing is collected 
gc.collect() 

Nếu bạn phải sử dụng "nhìn thấy" từ điển -like hoặc lịch sử, hãy cẩn thận rằng bạn giữ chỉ dữ liệu thực tế bạn cần và không có tham chiếu bên ngoài cho nó.

Tôi hơi thất vọng một chút bởi set_debug, tôi ước nó có thể được định cấu hình để xuất dữ liệu ở đâu đó khác với stderr, nhưng hy vọng that should change soon.

+0

gc.collect() trả về tất cả mọi thứ dưới dạng sưu tập và trả về lần gọi thứ hai 0. Điều đó có nghĩa là tôi không có bất kỳ chu kỳ nào đúng không? –

+0

@Paul: Không, bạn vẫn có thể có chu kỳ. Nhìn vào ví dụ cuối cùng tôi đã đưa ra: ở đây, gc.collect() trả lại 0, và không có gì được in. Nếu bạn có các chu kỳ đối tượng không có phương thức __del__, gc sẽ giữ im lặng. – NicDumZ

1

Bạn có sử dụng bất kỳ tiện ích mở rộng nào không? Họ là một nơi tuyệt vời cho rò rỉ bộ nhớ, và sẽ không được theo dõi bởi các công cụ python.

+0

Không có tiện ích mở rộng nào, nhưng là nơi thích hợp để những người khác tình cờ gặp phải. –

+0

Nếu bạn sử dụng Django ORM, bạn sử dụng mô-đun mở rộng - trình điều khiển cơ sở dữ liệu DB-API. Đây có phải là MySQLdb không? Phiên bản hiện tại đã biết rò rỉ bộ nhớ con trỏ khi kết nối được thiết lập với use_unicode = True (đó là trường hợp cho Django> = 1.0). – zgoda

+0

vâng, bạn có quyền trên tiền! Tôi đang sử dụng tất cả những thứ đó. Bất kỳ giải pháp được biết đến? –

19

DEBUG = Sai trong settings.py?

Nếu không Django sẽ vui vẻ lưu trữ tất cả các truy vấn SQL bạn thực hiện sẽ tăng lên.

+2

wow, tôi biết viết những từ django trong đó sẽ giúp đỡ. Có, tập lệnh của tôi không sử dụng settings.py sản xuất của tôi. * xấu hổ *. Cho phép xem nếu nó xóa vấn đề bộ nhớ. –

+0

Đây là nó! DEBUG được đặt thành True thực sự chiếm nhiều bộ nhớ khi chọn từ cơ sở dữ liệu lớn. –