2010-06-23 28 views
49

TL/DR:Dỡ bỏ một module bằng Python

import gc, sys 

print len(gc.get_objects()) # 4073 objects in memory 

# Attempt to unload the module 

import httplib 
del sys.modules["httplib"] 
httplib = None 

gc.collect() 
print len(gc.get_objects()) # 6745 objects in memory 

CẬP NHẬT Tôi đã liên lạc với các nhà phát triển Python về vấn đề này và thực sự nó not going to be possible to unload a module hoàn toàn "trong vòng năm năm tới". (xem liên kết)

Hãy chấp nhận rằng Python thực sự không hỗ trợ dỡ mô-đun cho các vấn đề kỹ thuật nghiêm trọng, cơ bản, không thể vượt qua, trong 2.x.


Trong săn gần đây của tôi cho một memleak trong ứng dụng của tôi, tôi đã thu hẹp nó xuống để mô-đun, cụ thể là không có khả năng của tôi để rác thu thập một module dỡ. Sử dụng bất kỳ phương thức nào được liệt kê bên dưới để hủy mô-đun để lại hàng nghìn đối tượng trong bộ nhớ. Nói cách khác - tôi không thể bỏ một mô-đun bằng Python ...

Phần còn lại của câu hỏi là cố gắng thu thập rác một mô-đun bằng cách nào đó.

Hãy thử:

import gc 
import sys 

sm = sys.modules.copy() # httplib, which we'll try to unload isn't yet 
         # in sys.modules, so, this isn't the source of problem 

print len(gc.get_objects()) # 4074 objects in memory 

Hãy lưu một bản sao của sys.modules cố gắng để khôi phục lại nó sau này. Vì vậy, đây là một đối tượng cơ sở 4074. Chúng ta nên quay trở lại lý tưởng bằng cách nào đó.

Hãy nhập một mô-đun:

import httplib 
print len(gc.get_objects()) # 7063 objects in memory 

Chúng tôi lên đến 7K đối tượng phi rác. Hãy thử xóa httplib từ sys.modules.

sys.modules.pop('httplib') 
gc.collect() 
print len(gc.get_objects()) # 7063 objects in memory 

Điều đó không hiệu quả. Hmm, nhưng không có tài liệu tham khảo trong số __main__? Ồ, vâng:

del httplib 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

Hoan hô, giảm 300 đối tượng. Tuy nhiên, không có điếu xì gà, đó là cách hơn 4000 vật thể ban đầu. Hãy thử khôi phục sys.modules từ bản sao.

sys.modules = sm 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

Hmmm, cũng đó là vô nghĩa, không có thay đổi .. Có lẽ nếu chúng ta quét sạch globals ...

globals().clear() 
import gC# we need this since gc was in globals() too 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

người dân địa phương?

locals().clear() 
import gC# we need this since gc was in globals() too 
gc.collect() 
print len(gc.get_objects()) # 6746 objects in memory 

Điều gì sẽ xảy ra nếu chúng tôi imported mô-đun bên trong exec?

local_dict = {} 
exec 'import httplib' in local_dict 
del local_dict 
gc.collect() 
print len(gc.get_objects()) # back to 7063 objects in memory 

Bây giờ, điều đó không công bằng, nó đã nhập nó vào __main__, tại sao? Nó sẽ không bao giờ rời khỏi local_dict ... Argh! Chúng tôi trở lại được nhập đầy đủ httplib. Có thể nếu chúng ta thay thế nó bằng một vật thể giả?

from types import ModuleType 
import sys 
print len(gc.get_objects()) # 7064 objects in memory 

Đẫm máu ..... !!

sys.modules['httplib'] = ModuleType('httplib') 
print len(gc.get_objects()) # 7066 objects in memory 

Mô-đun chết, chết !!

import httplib 
for attr in dir(httplib): 
    setattr(httplib, attr, None) 
gc.collect() 
print len(gc.get_objects()) # 6749 objects in memory 

Được rồi, sau khi tất cả những nỗ lực, tốt nhất là 2675 (gần + 50%) từ điểm xuất phát ... Đó chỉ là từ một mô-đun ... Điều đó thậm chí không có gì lớn bên trong ...

Ok, bây giờ nghiêm túc, lỗi của tôi ở đâu? Làm cách nào để xóa mô-đun và xóa tất cả nội dung của mô-đun? Hoặc là mô-đun của Python một rò rỉ bộ nhớ khổng lồ?

nguồn đầy đủ trong đơn giản để sao chép hình thức: http://gist.github.com/450606

Trả lời

17

Python không hỗ trợ module dỡ.

Tuy nhiên, trừ khi chương trình của bạn tải số lượng mô-đun không giới hạn theo thời gian, đó không phải là nguồn rò rỉ bộ nhớ của bạn. Modules thường được nạp một lần khi khởi động và đó là nó. Rò rỉ bộ nhớ của bạn rất có thể nằm ở nơi khác.

Trong trường hợp không chắc chương trình của bạn thực sự tải số lượng mô-đun không giới hạn theo thời gian, bạn có lẽ nên thiết kế lại chương trình của mình. ;-)

+1

Có, nó tải không giới hạn số lượng mô-đun - đó là một máy chủ ứng dụng web chấp nhận sửa đổi mới của mã nguồn riêng của nó và tải lại nó (đó là nhiệm vụ web khá chuẩn). Sự rò rỉ IS từ thực tế là mã cũ vẫn còn tồn tại trong bộ nhớ, ngay cả khi được thay thế, ngay cả khi không thể truy cập được ... –

+0

Python hỗ trợ các mô-đun dỡ tải. Chúng được thu thập rác, giống như mọi đối tượng khác trong Python. –

+1

@Slava: Bạn có thể muốn xem mã nguồn để 'mod_python', có trình nhập riêng của nó được thiết kế để xử lý nạp lại các mô-đun mà không tạo rò rỉ bộ nhớ. Có thể có một số mã trong đó bạn có thể sử dụng. –

0

(Bạn nên cố gắng viết những câu hỏi ngắn gọn hơn, tôi đã chỉ đọc đầu và lướt phần còn lại.) Tôi thấy một vấn đề đơn giản khi bắt đầu:

sm = sys.modules.copy() 

Bạn làm một bản sao của sys.modules, vì vậy bây giờ bản sao của bạn có tham chiếu đến mô-đun - vì vậy tất nhiên nó sẽ không được thu thập. Bạn có thể xem những gì đề cập đến nó với gc.get_referrers.

này hoạt động tốt:

# module1.py 
class test(object): 
    def __del__(self): 
     print "unloaded module1" 
a = test() 

print "loaded module1" 

.

# testing.py 
def run(): 
    print "importing module1" 
    import module1 
    print "finished importing module1" 

def main(): 
    run() 
    import sys 
    del sys.modules["module1"] 
    print "finished" 

if __name__ == '__main__': 
    main() 

module1 được tải ngay khi chúng tôi xóa khỏi sys.modules, vì không còn tham chiếu đến mô-đun. (Thực hiện sau khi nhập cũng sẽ hoạt động - tôi chỉ cần nhập khẩu vào một chức năng khác để rõ ràng. Tất cả những gì bạn phải làm là xóa các tham chiếu đến nó.)

Bây giờ, có một chút khó khăn khi thực hiện điều này trong thực tế , do hai vấn đề:

  • Để thu thập mô-đun, tất cả tham chiếu đến mô-đun phải không thể truy cập được (như thu thập bất kỳ đối tượng nào). Điều đó có nghĩa là bất kỳ mô-đun nào khác đã nhập khẩu cũng cần được hủy đăng ký và tải lại.
  • Nếu bạn xóa mô-đun khỏi sys.modules khi nó vẫn được tham chiếu ở một nơi khác, bạn đã tạo ra một tình huống bất thường: mô-đun vẫn được tải và sử dụng theo mã, nhưng trình tải mô-đun không biết về nó nữa. Lần sau khi bạn nhập mô-đun, bạn sẽ không nhận được tham chiếu đến mô-đun hiện tại (vì bạn đã xóa bản ghi đó), do đó, nó sẽ tải bản sao mô-đun thứ hai cùng tồn tại. Điều này có thể gây ra vấn đề nghiêm trọng nhất quán. Vì vậy, hãy chắc chắn rằng không có tham chiếu còn lại cho mô-đun trước khi cuối cùng loại bỏ nó khỏi sys.modules.

Có một số vấn đề khó khăn để sử dụng điều này nói chung: phát hiện mô-đun nào phụ thuộc vào mô-đun bạn đang tải; biết liệu có ổn không để tải những thứ đó (phụ thuộc rất nhiều vào trường hợp sử dụng của bạn); xử lý luồng trong khi kiểm tra tất cả điều này (hãy xem imp.acquire_lock), v.v.

Tôi có thể tạo ra một trường hợp khi thực hiện việc này có thể hữu ích, nhưng hầu hết thời gian tôi khuyên bạn chỉ nên khởi động lại ứng dụng nếu mã của nó thay đổi. Có thể bạn sẽ chỉ bị đau đầu.

+8

Vâng, không phải là snyde, nhưng bạn nên đã đọc câu hỏi, hoặc ít nhất là từ "hoàn toàn" trong tiêu đề (hoặc ít nhất là các thẻ). Vấn đề không phải là tôi không tải lại, vấn đề là * rò rỉ bộ nhớ * liên kết với bất kỳ loại bỏ (liệt kê) loại nào (bao gồm cả những cái mà bạn đã đề xuất, * được * liệt kê trong câu hỏi của tôi, cùng với tá người khác). Thực ra tôi đã thêm 'sys.modules.copy()' vào một giai đoạn rất muộn, loại bỏ nó không thay đổi bất cứ điều gì (thử bản thân). –

+1

Nguồn, để thử: http://gist.github.com/450606. Hãy thử xóa sys.modules.copy và bạn sẽ thấy rằng vẫn còn có hơn 50% tăng đối tượng ngay cả khi tất cả các tham chiếu đến mô-đun đã bị xóa. –

+0

Xem tại đây để biết về những gì sai (sử dụng mã của bạn): http://gist.github.com/450726. Tôi không cố gắng load-unload 'sys', vì chúng ta đang hoạt động trên' sys.modules', vì vậy tôi sử dụng 'httplib' - bạn có thể thử bất kỳ cái nào khác. –

3

Tôi không chắc chắn về Python, nhưng bằng các ngôn ngữ khác, gọi số gc.collect() không không phát hành bộ nhớ không sử dụng - nó sẽ chỉ phát hành bộ nhớ đó nếu/khi bộ nhớ thực sự cần thiết.

Nếu không, sẽ có ý nghĩa khi Python giữ mô-đun trong bộ nhớ trong thời gian này, trong trường hợp chúng cần được tải lại.

+0

Vấn đề là tôi cần phải thay thế chúng bằng các phiên bản mới. Và ngay cả khi tôi thay thế nó 1-to-1 với mô-đun kích thước tương tự - việc sử dụng bộ nhớ phát triển (rò rỉ) ... Cảm ơn lời đề nghị, mặc dù. –

Các vấn đề liên quan