我正在与Pympler进行一些完整性检查,以确保在我尝试分析实际脚本时我理解结果,但我对结果感到有些困惑。以下是我尝试的健全性检查:
SANITY CHECK 1:我启动Python(3)控制台并执行以下操作:
from pympler import summary, muppy
sum = summary.summarize(muppy.get_objects())
summary.print_(sum)
这导致以下摘要:
types | # objects | total size
==================================== | =========== | ============
<class 'str | 16047 | 1.71 MB
<class 'dict | 2074 | 1.59 MB
<class 'type | 678 | 678.27 KB
<class 'code | 4789 | 673.68 KB
<class 'set | 464 | 211.00 KB
<class 'list | 1319 | 147.16 KB
<class 'tuple | 1810 | 120.03 KB
<class 'weakref | 1269 | 99.14 KB
<class 'wrapper_descriptor | 1124 | 87.81 KB
<class 'builtin_function_or_method | 918 | 64.55 KB
<class 'abc.ABCMeta | 64 | 62.25 KB
<class 'method_descriptor | 877 | 61.66 KB
<class 'int | 1958 | 58.88 KB
<class 'getset_descriptor | 696 | 48.94 KB
function (__init__) | 306 | 40.64 KB
如果我刚刚启动了一个新的Python会话,那么内存中的所有这些字符串,字典,列表等都是如何?我不认为Pympler会在所有会话中总结结果(这没有任何意义,但这是我能想到的唯一可能性)。
SANITY CHECK 2:由于我不太了解tabula rasa Python会话的摘要结果,所以在定义一些变量/数据结构之后,让我们看一下总结的不同之处。我打开另一个控制台并执行以下操作:
from pympler import summary, muppy
sum = summary.summarize(muppy.get_objects())
a = {}
b = {}
c = {}
d = {'a': [0, 0, 1, 2], 't': [3, 3, 3, 1]}
sum1 = summary.summarize(muppy.get_objects())
summary.print_(summary.get_diff(sum, sum1))
这导致以下摘要:
types | # objects | total size
============================== | =========== | ============
<class 'list | 3247 | 305.05 KB
<class 'str | 3234 | 226.04 KB
<class 'int | 552 | 15.09 KB
<class 'dict | 1 | 480 B
function (_keys) | 0 | 0 B
function (get_path) | 0 | 0 B
function (http_open) | 0 | 0 B
function (memoize) | 0 | 0 B
function (see) | 0 | 0 B
function (recvfrom) | 0 | 0 B
function (rfind) | 0 | 0 B
function (wm_focusmodel) | 0 | 0 B
function (_parse_makefile) | 0 | 0 B
function (_decode_pax_field) | 0 | 0 B
function (__gt__) | 0 | 0 B
我以为我刚刚初始化了四个新词典(尽管3个是空的),为什么Muppy只显示了一个新词典对象的区别?此外,为什么有成千上万的新字符串和列表,更不用说整数?
SANITY CHECK 3:我再次开始一个新的Python会话,但这一次想看看Pympler如何处理更复杂的数据类型,如字典列表。
from pympler import muppy, summary
sum = summary.summarize(muppy.get_objects())
a = [{}, {}, {}, {'a': [0, 0, 1, 2], 't': [3, 3, 3, 1]}, {'a': [1, 2, 3, 4]}]
sum1 = summary.summarize(muppy.get_objects())
summary.print_(summary.get_diff(sum, sum1))
结果如下:
types | # objects | total size
===================================================== | =========== | ============
<class 'list | 3233 | 303.88 KB
<class 'str | 3270 | 230.71 KB
<class 'int | 554 | 15.16 KB
<class 'dict | 10 | 5.53 KB
<class 'code | 16 | 2.25 KB
<class 'type | 2 | 1.98 KB
<class 'tuple | 6 | 512 B
<class 'getset_descriptor | 4 | 288 B
function (__init__) | 2 | 272 B
<class '_frozen_importlib_external.SourceFileLoader | 3 | 168 B
<class '_frozen_importlib.ModuleSpec | 3 | 168 B
<class 'weakref | 2 | 160 B
function (__call__) | 1 | 136 B
function (Find) | 1 | 136 B
function (<lambda>) | 1 | 136 B
即使列表和词典嵌套有点令人费解,但根据我的统计,我添加了5个新词典和4个新列表。
有人能解释一下Muppy是如何计算对象的吗?
答案 0 :(得分:1)
get_objects
在新的Python会话中 summary.summarize(muppy.get_objects())
返回启动期间和运行from pympler import summary, muppy
时实例化的所有对象,这说明了数量众多。
get_objects
调用之间的区别请记住,由sum
生成的summary.summarize()
对象是在第一个快照之后创建的,该快照解释了“成千上万的新字符串和列表”。您可以通过将测试重写为以下方式来解决此问题:
from pympler import summary, muppy
o1 = muppy.get_objects()
a = {}
b = {}
c = {}
d = {'a': [0, 0, 1, 2], 't': [3, 3, 3, 1]}
o2 = muppy.get_objects()
summary.print_(summary.get_diff(summary.summarize(o1), summary.summarize(o2)))
这会将多余的差异减少到o1
和其他两个对象的大列表中:
>>> for o in diff['+']:
... print("%s - %s" % (type(o), o if len(o) < 10 else "long list"))
...
<class 'str'> - o2
<class 'list'> - long list
<class 'dict'> - {'a': [0, 0, 1, 2], 't': [3, 3, 3, 1]}
<class 'list'> - ['o2', 'muppy', 'get_objects']
<class 'list'> - [0, 0, 1, 2]
<class 'list'> - [3, 3, 3, 1]
要了解这一点,我们需要知道pympler到底在检查什么。
muppy.get_objects
实现relies
gc.get_objects()
,它是“收集器跟踪的所有对象的列表”(gc.is_tracked
),但堆栈框架除外。
不会跟踪原子类型的实例,而不会跟踪非原子类型的实例(容器,用户定义的对象……)。但是,可以提供一些特定于类型的优化,以抑制简单实例的垃圾收集器占用空间(例如,仅包含原子键和值的字典)
如果您按照上述建议存储对象列表__flags__
,并检查使用了哪些对象:
o2
您会看到:
空dict不受GC跟踪,并且由于它们仅从局部变量中引用,因此muppy不能解释它们:
def tracked(obj_list, obj):
import gc
return {"tracked_by_muppy": any(id(item) == id(obj) for item in obj_list),
"gc_tracked": gc.is_tracked(obj)}
简单的字典tracked(o2, a) # => {'tracked_by_muppy': False, 'gc_tracked': False}
是GC跟踪的,因此出现在muppy报告中:
d