摘要:一般情況下,的垃圾收集器會被用于檢測上面這樣的循環引用,并刪除掉它們。你可以通過強制垃圾收集器運行,并檢查列表里有什么來驗證上述結論。
-- [since Python 3.4, circular references are handled much better](http://engineering.hearsaysocial.com/2013/06/16/circular-references-in-python/#comment-2882030670) Nice post. Note that starting from Python 3.4, circular references are handled much better (docs imply it should be rare that they are not collected -- but don"t give specifics about how to make that happen). For example the example you give is no longer a problem in Python 3.5 (probably not in 3.4 either, but can"t test it right now).前言
用像 Python, Ruby 這樣的解釋型語言編程很方便的一個方面就是,通常情況下,你可以避免處理內存管理相關的事情。然而,有一個眾所周知的情況 Python 一定會有內存泄漏,這就是當你在對象創建中聲明了一個循環引用,而且在類聲明中實現了一個自定義的 __del__ 解構方法。例如,考慮如下例子:
One of the more convenient aspects of writing code in interpreted languages such as Python or Ruby is that you normally can avoid dealing with memory management. However, one known case where Python will definitely leak memory is when you declare circular references in your object declarations and implement a custom __del__ destructor method in one these classes. For instance, consider the following example:
class A(object): def __init__(self, b_instance): self.b = b_instance class B(object): def __init__(self): self.a = A(self) def __del__(self): print "die" def test(): b = B() test()
當函數 test() 被調用時,它聲明了一個對象 B,在 B 的 __init__ 函數中,把自己當成變量傳給了 A,A 然后在 __init__ 函數中聲明了對 B 的引用,這就造成了一個循環引用。一般情況下,python 的垃圾收集器會被用于檢測上面這樣的循環引用,并刪除掉它們。然而,因為自定義的 ___del__ 方法,垃圾收集器會把這個循環引用相關對象標記為 “uncollectable”。從設計上說,垃圾收集器并不知道循環引用對象的銷毀順序,所以也就不會去處理它們。你可以通過強制垃圾收集器運行,并檢查 gc.garbage 列表里有什么來驗證上述結論。
When the function test() is invoked, it declares an instance of B, which passes itself to A, which then sets a reference to B, resulting in a circular reference. Normally Python"s garbage collector, which is used to detect these types of cyclic references, would remove it. However, because of the custom destructor (the __del__ method), it marks this item as "uncollectable". By design, it doesn"t know the order in which to destroy the objects, so leaves them alone (see Python"s garbage collection documentation for more background). You can verify this aspect by forcing the Python garbage collector to run and inspecting what is set inside the gc.garbage array:
import gc gc.collect() print gc.garbage [<__main__.B object at 0x7f59f57c98d0>]
你可以通過 objgraph 庫可視化這些循環引用。
You can also see these circular references visually by using the objgraph library, which relies on Python"s gc module to inspect the references to your Python objects. Note that objgraph library also deliberately plots the the custom __del__ methods in a red circle to spotlight a possible issue.
為了避免循環引用,你通常需要使用 weak reference,向 python 解釋器聲明:如果剩余的引用屬于 weak reference,或者使用了 context manager 或 with 語法,那么內存可以被垃圾收集器回收并用于重新聲明對象。
To avoid circular references, you usually need to use weak references, declaring to the interpreter that the memory can be reclaimed for an object if the remaining references are of these types, or to use context managers and the with statement (for an example of this latter approach, see how it was solved for the happybase library).
find_circular_references.py# -*- encoding: utf-8 -*- from __future__ import print_function import gc import traceback import types from tornado import web, ioloop, gen from tornado.http1connection import HTTP1ServerConnection def find_circular_references(garbage=None): """ 從 garbage 中尋找循環引用 """ def inner(level): """ 處理內層的數據 """ for item in level: item_id = id(item) if item_id not in garbage_ids: continue if item_id in visited_ids: continue if item_id in stack_ids: candidate = stack[stack.index(item):] candidate.append(item) found.append(candidate) continue stack.append(item) stack_ids.add(item_id) inner(gc.get_referents(item)) stack.pop() stack_ids.remove(item_id) visited_ids.add(item_id) ######### 開始初始化 ######## # 獲取傳入的 garbage 或者通過 gc 模塊獲取 garbage 列表 garbage = garbage or gc.garbage # 已經找到的循環引用列表 type: list of list found = [] # 存放 item 的堆 stack = [] # 存放 item_id 的 set stack_ids = set() # 保存 garbage 里每個對象的 id garbage_ids = set(map(id, garbage)) # 保存 visited item 的 id visited_ids = set() ######## 初始化結束 ######## # 進入遞歸函數 inner inner(garbage) inner = None return found class CollectHandler(web.RequestHandler): @gen.coroutine def get(self): # collect_result = None collect_result = gc.collect() garbage = gc.garbage # for i in garbage[:5]: # print(gc.get_referents(i), " ") self.write("Collected: {} ".format(collect_result)) self.write("Garbage: {} ".format(len(gc.garbage))) for circular in find_circular_references(): print(" ========== Circular ==========") for item in circular: print(" ", repr(item)) for item in circular: if isinstance(item, types.FrameType): print(" Locals:", item.f_locals) print(" Traceback:", repr(item)) traceback.print_stack(item) class DummyHandler(web.RequestHandler): @gen.coroutine def get(self): self.write("ok ") self.finish() application = web.Application([ (r"/dummy/", DummyHandler), (r"/collect/", CollectHandler), ], debug=True) if __name__ == "__main__": gc.disable() gc.collect() gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK) print("GC disabled") print("Start on 8888") application.listen(8888) ioloop.IOLoop.current().start()
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/40677.html
摘要:每個屬性參數構造函數中值的類型都能夠被成字符串類型。對比上文給出的個代碼片段,可發現皆在用不同的方法進行元數據配置,并且被配置的具體對象是數據庫驅動。 @(SPRING FRAMEWORK) 〔4〕7.4 Dependencies 聲明: 斜體字:《官檔》原文 斜體加粗字:《官檔》原文的重點字、詞、句 正體字+前置〔〕:個人表述行為,如:〔總結〕、〔分析〕等 灰體字:生詞 粉體...
摘要:正好最近在學習的各種實現原理,在這里斗膽翻譯一篇垃圾回收機制原文鏈接。自動管理的機制中,通常都會包含垃圾回收機制。二垃圾回收機制的概念垃圾回收,是一種自動管理應用程序所占內存的機制,簡稱方便起見,本文均采用此簡寫。 最近關注了一個國外技術博客RisingStack里面有很多高質量,且對新手也很friendly的文章。正好最近在學習Node.js的各種實現原理,在這里斗膽翻譯一篇Node...
摘要:開源安裝通過通過使用方法作為運行請先確保當前主機已經安裝和啟動通過命令啟動訪問假設運行于端口訪問以獲取某個爬蟲任務的日志分析詳情配合實現爬蟲進度可視化詳見在代碼中使用 GitHub 開源 my8100 / logparser 安裝 通過 pip: pip install logparser 通過 git: git clone https://github.com/my8100/logp...
摘要:摘要是如何回收內存的深入淺出系列深入淺出第課箭頭函數中的究竟是什么鬼深入淺出第課函數是一等公民是什么意思呢深入淺出第課什么是垃圾回收算法最近垃圾回收這個話題非常火,大家不能隨隨便便的扔垃圾了,還得先分類,這樣方便對垃圾進行回收再利用。 摘要: JS是如何回收內存的? 《JavaScript深入淺出》系列: JavaScript深入淺出第1課:箭頭函數中的this究竟是什么鬼? Jav...
摘要:是如何工作的內存管理以及如何處理四種常見的內存泄漏原文譯者幾個禮拜之前我們開始一系列對于以及其本質工作原理的深入挖掘我們認為通過了解的構建方式以及它們是如何共同合作的,你就能夠寫出更好的代碼以及應用。 JavaScript是如何工作的:內存管理以及如何處理四種常見的內存泄漏 原文:How JavaScript works: memory management + how to han...
閱讀 2368·2021-11-18 10:07
閱讀 2318·2021-09-22 15:59
閱讀 3077·2021-08-23 09:42
閱讀 2276·2019-08-30 15:44
閱讀 1191·2019-08-29 15:06
閱讀 2303·2019-08-29 13:27
閱讀 1210·2019-08-29 13:21
閱讀 1412·2019-08-29 13:13