– 跟踪内存分配 – 调试和分析(Python教程)(参考资料)
tracemalloc
– 跟踪内存分配
版本3.4.
源代码:LIB / tracemalloc.py
tracemalloc模块是一个用于跟踪由Python分配的内存块的调试工具。它提供以下信息:
要跟踪Python分配的大多数内存块,应通过设置PYTHONTRACEMALLOC
环境变量为1
,或者使用-X
tracemalloc
命令行选项。tracemalloc.start()
函数可以在运行时调用tostart跟踪Python内存分配.
默认情况下,分配的内存块的跟踪仅存储最近的帧(1帧)。在启动时存储25帧:将PYTHONTRACEMALLOC
环境变量设置为25
,或使用-X
tracemalloc=25
命令行选项.
例子
显示前10个
显示分配最多内存的10个文件:
import tracemalloctracemalloc.start()# ... run your application ...snapshot = tracemalloc.take_snapshot()top_stats = snapshot.statistics("lineno")print("[ Top 10 ]")for stat in top_stats[:10]: print(stat)
Python测试套件的输出示例:
[ Top 10 ]<frozen importlib._bootstrap>:716: size=4855 KiB, count=39328, average=126 B<frozen importlib._bootstrap>:284: size=521 KiB, count=3199, average=167 B/usr/lib/python3.4/collections/__init__.py:368: size=244 KiB, count=2315, average=108 B/usr/lib/python3.4/unittest/case.py:381: size=185 KiB, count=779, average=243 B/usr/lib/python3.4/unittest/case.py:402: size=154 KiB, count=378, average=416 B/usr/lib/python3.4/abc.py:133: size=88.7 KiB, count=347, average=262 B<frozen importlib._bootstrap>:1446: size=70.4 KiB, count=911, average=79 B<frozen importlib._bootstrap>:1454: size=52.0 KiB, count=25, average=2131 B<string>:5: size=49.7 KiB, count=148, average=344 B/usr/lib/python3.4/sysconfig.py:411: size=48.0 KiB, count=1, average=48.0 KiB
我们可以看到Python加载了4855 KiB
数据(字节码和常量)frommodules和collections
模块分配244 KiB
构建namedtuple
types.
查看Snapshot.statistics()
更多选项.
计算差异
拍摄两张快照并显示差异:
import tracemalloctracemalloc.start()# ... start your application ...snapshot1 = tracemalloc.take_snapshot()# ... call the function leaking memory ...snapshot2 = tracemalloc.take_snapshot()top_stats = snapshot2.compare_to(snapshot1, "lineno")print("[ Top 10 differences ]")for stat in top_stats[:10]: print(stat)
运行Python测试套件的一些测试之前/之后的输出示例:
[ Top 10 differences ]<frozen importlib._bootstrap>:716: size=8173 KiB (+4428 KiB), count=71332 (+39369), average=117 B/usr/lib/python3.4/linecache.py:127: size=940 KiB (+940 KiB), count=8106 (+8106), average=119 B/usr/lib/python3.4/unittest/case.py:571: size=298 KiB (+298 KiB), count=589 (+589), average=519 B<frozen importlib._bootstrap>:284: size=1005 KiB (+166 KiB), count=7423 (+1526), average=139 B/usr/lib/python3.4/mimetypes.py:217: size=112 KiB (+112 KiB), count=1334 (+1334), average=86 B/usr/lib/python3.4/http/server.py:848: size=96.0 KiB (+96.0 KiB), count=1 (+1), average=96.0 KiB/usr/lib/python3.4/inspect.py:1465: size=83.5 KiB (+83.5 KiB), count=109 (+109), average=784 B/usr/lib/python3.4/unittest/mock.py:491: size=77.7 KiB (+77.7 KiB), count=143 (+143), average=557 B/usr/lib/python3.4/urllib/parse.py:476: size=71.8 KiB (+71.8 KiB), count=969 (+969), average=76 B/usr/lib/python3.4/contextlib.py:38: size=67.2 KiB (+67.2 KiB), count=126 (+126), average=546 B
我们可以看到Python已经加载了8173 KiB
的模块数据(字节码和常量),并且这比在测试之前加载的时间多4428 KiB
,这是在拍摄上一个快照时。同样地,linecache
模块已缓存940 KiB
的Python源代码来格式化回溯,所有这些都是自上一个快照以来
如果系统的可用内存很少,快照可以是使用Snapshot.dump()
方法写在磁盘上以离线分析快照。然后使用Snapshot.load()
方法重新加载快照.
获取内存块的回溯
代码显示最大内存块的回溯:
import tracemalloc# Store 25 framestracemalloc.start(25)# ... run your application ...snapshot = tracemalloc.take_snapshot()top_stats = snapshot.statistics("traceback")# pick the biggest memory blockstat = top_stats[0]print("%s memory blocks: %.1f KiB" % (stat.count, stat.size / 1024))for line in stat.traceback.format(): print(line)
Python测试套件的输出示例(回溯限制为25帧):
903 memory blocks: 870.1 KiB File "<frozen importlib._bootstrap>", line 716 File "<frozen importlib._bootstrap>", line 1036 File "<frozen importlib._bootstrap>", line 934 File "<frozen importlib._bootstrap>", line 1068 File "<frozen importlib._bootstrap>", line 619 File "<frozen importlib._bootstrap>", line 1581 File "<frozen importlib._bootstrap>", line 1614 File "/usr/lib/python3.4/doctest.py", line 101 import pdb File "<frozen importlib._bootstrap>", line 284 File "<frozen importlib._bootstrap>", line 938 File "<frozen importlib._bootstrap>", line 1068 File "<frozen importlib._bootstrap>", line 619 File "<frozen importlib._bootstrap>", line 1581 File "<frozen importlib._bootstrap>", line 1614 File "/usr/lib/python3.4/test/support/__init__.py", line 1728 import doctest File "/usr/lib/python3.4/test/test_pickletools.py", line 21 support.run_doctest(pickletools) File "/usr/lib/python3.4/test/regrtest.py", line 1276 test_runner() File "/usr/lib/python3.4/test/regrtest.py", line 976 display_failure=not verbose) File "/usr/lib/python3.4/test/regrtest.py", line 761 match_tests=ns.match_tests) File "/usr/lib/python3.4/test/regrtest.py", line 1563 main() File "/usr/lib/python3.4/test/__main__.py", line 3 regrtest.main_in_temp_cwd() File "/usr/lib/python3.4/runpy.py", line 73 exec(code, run_globals) File "/usr/lib/python3.4/runpy.py", line 160 "__main__", fname, loader, pkg_name)
我们可以看到大多数内存都是从importlib
模块中分配来自modules:870.1 KiB
的toload数据(字节码和常量)。回溯是importlib
最近加载的数据:import pdb
模块的doctest
行。如果加载了一个新模块,回溯可能会改变.
Pretty top
代码显示10行分配最大内存的漂亮输出,忽略<frozen importlib._bootstrap>
和<unknown>
files:
import linecacheimport osimport tracemallocdef display_top(snapshot, key_type="lineno", limit=10): snapshot = snapshot.filter_traces(( tracemalloc.Filter(False, "<frozen importlib._bootstrap>"), tracemalloc.Filter(False, "<unknown>"), )) top_stats = snapshot.statistics(key_type) print("Top %s lines" % limit) for index, stat in enumerate(top_stats[:limit], 1): frame = stat.traceback[0] # replace "/path/to/module/file.py" with "module/file.py" filename = os.sep.join(frame.filename.split(os.sep)[-2:]) print("#%s: %s:%s: %.1f KiB" % (index, filename, frame.lineno, stat.size / 1024)) line = linecache.getline(frame.filename, frame.lineno).strip() if line: print(" %s" % line) other = top_stats[limit:] if other: size = sum(stat.size for stat in other) print("%s other: %.1f KiB" % (len(other), size / 1024)) total = sum(stat.size for stat in top_stats) print("Total allocated size: %.1f KiB" % (total / 1024))tracemalloc.start()# ... run your application ...snapshot = tracemalloc.take_snapshot()display_top(snapshot)
Python测试套件的输出示例:
Top 10 lines#1: Lib/base64.py:414: 419.8 KiB _b85chars2 = [(a + b) for a in _b85chars for b in _b85chars]#2: Lib/base64.py:306: 419.8 KiB _a85chars2 = [(a + b) for a in _a85chars for b in _a85chars]#3: collections/__init__.py:368: 293.6 KiB exec(class_definition, namespace)#4: Lib/abc.py:133: 115.2 KiB cls = super().__new__(mcls, name, bases, namespace)#5: unittest/case.py:574: 103.1 KiB testMethod()#6: Lib/linecache.py:127: 95.4 KiB lines = fp.readlines()#7: urllib/parse.py:476: 71.8 KiB for a in _hexdig for b in _hexdig}#8: <string>:5: 62.0 KiB#9: Lib/_weakrefset.py:37: 60.0 KiB self.data = set()#10: Lib/base64.py:142: 59.8 KiB _b32tab2 = [a + b for a in _b32tab for b in _b32tab]6220 other: 3602.8 KiBTotal allocated size: 5303.1 KiB
参见Snapshot.statistics()
了解更多选项.
API
函数
tracemalloc.
clear_traces
()-
清除Python分配的内存块的痕迹
参见
stop()
.
tracemalloc.
get_object_traceback
(obj)-
获取分配了Python对象obj的回溯。返回
Traceback
实例,或None
如果tracemalloc
模块没有跟踪内存分配或没有跟踪对象的分配.参见
gc.get_referrers()
和sys.getsizeof()
functions.
tracemalloc.
get_traceback_limit
()-
获取跟踪回溯中存储的最大帧数
tracemalloc
模块必须跟踪内存分配以达到限制,否则会引发异常.限制由
start()
功能设定
tracemalloc.
get_traced_memory
()-
获取当前大小和记忆的峰值大小
tracemalloc
模块跟踪的块作为元组:(current: int, peak: int)
.
tracemalloc.
get_tracemalloc_memory
()-
获取
tracemalloc
模块的内存使用量(以字节为单位)用于存储内存块的痕迹。如果int
.
tracemalloc.
is_tracing
模块正在跟踪Python内存分配,则返回(-
True
)tracemalloc
,否则False
否则.参见
start()
和stop()
functions.
tracemalloc.
start
(nframe: int=1)-
开始追踪Python内存分配:在Python memoryallocators上安装钩子。收集的跟踪回溯将限制为nframe帧。默认情况下,内存块的跟踪仅存储最新的帧:限制为
1
. nframe必须大于或等于1
.存储超过
1
frame仅对计算由"traceback"
或者计算累积统计数据:请参阅Snapshot.compare_to()
和Snapshot.statistics()
methods.调整更多帧会增加
tracemalloc
模块的内存和CPU开销。使用get_tracemalloc_memory()
函数来测量tracemalloc
模块使用了多少内存PYTHONTRACEMALLOC
环境变量(PYTHONTRACEMALLOC=NFRAME
)和-X
tracemalloc=NFRAME
命令行选项可用于在启动时开始跟踪.参见
stop()
,is_tracing()
和get_traceback_limit()
functions.
tracemalloc.
stop
( )-
停止跟踪Python内存分配:在Python内存分配器上卸载挂钩。同时清除所有以前收集的由Python分配的内存块的痕迹.
Call
take_snapshot()
在清理之前拍摄痕迹的快照.参见
start()
,is_tracing()
和clear_traces()
functions.
DomainFilter
- class
tracemalloc.
DomainFilter
(inclusive: bool, domain: int) -
通过地址空间(域)过滤内存块的痕迹.
版本3.6.
inclusive
-
如果inclusive是
True
(include),匹配地址空间中分配的内存块domain
.如果inclusive是
False
(不包括),匹配地址空间中未分配的内存块domain
.
domain
-
地址记忆块的空间(
int
)。只读属性.
过滤
- class
tracemalloc.
Filter
(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False, domain: int=None) -
过滤内存块的痕迹.
见
fnmatch.fnmatch()
函数为filename_pattern。".pyc"
文件扩展名用".py"
.例子:
Filter(True, subprocess.__file__)
只包括subprocess
模块Filter(False, tracemalloc.__file__)
不包括tracemalloc
模块Filter(False, "<unknown>")
排除空回溯
在版本3.5中更改:
".pyo"
文件扩展名不再替换为".py"
.在版本3.6中更改:添加了
domain
attribute.domain
-
内存块的地址空间(
int
或None
).tracemalloc使用域
0
跟踪内存分配由Python制作。C扩展可以使用其他域来跟踪其他资源.
inclusive
-
如果inclusive是
True
(包含),只匹配在名称匹配的文件中分配的内存块filename_pattern
在行号lineno
.如果inclusive是
False
(不包括),忽略在行号//中匹配filename_pattern
的文件中分配的内存块lineno
.
lineno
-
过滤器的行号(
int
)如果lineno是None
,则过滤器匹配任何行号.
filename_pattern
-
过滤器的文件名模式(
str
)。只读属性
all_frames
-
如果all_frames是
True
,则检查所有回溯帧。如果all_frames是False
,则仅检查最近的帧.如果回溯限制为
1
,则此属性无效。见get_traceback_limit()
功能和Snapshot.traceback_limit
属性
帧
- class
tracemalloc.
Frame
-
追溯框架
Traceback
class是Frame
instances.filename
-
文件名 (
str
).
lineno
-
电话号码 (
int
).
快照
- class
tracemalloc.
Snapshot
-
Python分配的内存块跟踪快照.
take_snapshot()
function创建快照实例.compare_to
(old_snapshot: Snapshot, key_type: str, cumulative: bool=False)-
用旧快照计算差异。获取统计数据作为
StatisticDiff
实例的分类列表,按key_type.分组查看
Snapshot.statistics()
方法key_type和cumulativeparameters.结果从最大到最小排序为:
StatisticDiff.size_diff
,StatisticDiff.size
的绝对值,StatisticDiff.count_diff
,Statistic.count
的绝对值,然后是StatisticDiff.traceback
.
dump
(filename)-
将快照写入文件
使用
load()
重新加载快照.
filter_traces
(filters)-
用过滤的
Snapshot
序列创建一个新的traces
实例,filters是DomainFilter
和Filter
实例的列表。如果filters是一个空列表,则返回一个新的Snapshot
带有痕迹副本的实例.一次性应用全包过滤器,如果无包含过滤器匹配,则忽略跟踪。如果至少有一个独占过滤器匹配,则忽略跟踪.
版本3.6更改:
DomainFilter
实例现在也被filters.
- classmethod
load
(filename) -
从文件加载快照
另见
dump()
.
statistics
(key_type: str, cumulative: bool=False)-
获取统计数据作为
Statistic
实例分组key_type:key_type的 描述 "filename"
文件名 "lineno"
文件名和行号 "traceback"
追溯 如果cumulative是
True
,累积大小和跟踪回溯的所有帧的内存块数,不仅是最近的帧。累积模式只能用于key_type等于"filename"
和"lineno"
.结果从最大到最小排序:
Statistic.size
,Statistic.count
然后按Statistic.traceback
.
traceback_limit
-
回溯中存储的最大帧数
traces
:get_traceback_limit()
快照拍摄时
traces
-
Python分配的所有内存块的跟踪:
Trace
实例的序列序列具有未定义的顺序。使用
Snapshot.statistics()
方法获取统计的排序列表.
Statistic
- class
tracemalloc.
Statistic
-
关于内存分配的统计.
Snapshot.statistics()
返回Statistic
实例的列表也见
StatisticDiff
class.count
-
内存块的数量(
int
).
size
-
内存块的总大小以字节为单位(
int
).
traceback
-
分配内存块的回溯,
Traceback
instance.
StatisticDiff
- class
tracemalloc.
StatisticDiff
-
新老内存分配的统计差异
Snapshot
instance.Snapshot.compare_to()
返回StatisticDiff
实例。另见Statistic
class.count
-
新快照中的内存块数量(
int
):0
如果新快照中已释放内存块.
count_diff
-
旧快照和新闻快照之间的内存块数量差异(
int
):0
如果已在新快照中分配了内存块
size
-
新快照中内存块的总大小(以字节为单位)(
int
):0
如果新快照中已释放内存块.
size_diff
-
旧快照和新快照之间的内存块总大小(以字节为单位)的差异(
int
):0
如果内存块已在新快照中分配了
traceback
-
Traceback其中分配了内存块,
Traceback
instance.
Trace
- class
tracemalloc.
Trace
-
追踪内存块
Snapshot.traces
属性是一系列的Trace
instances.更改版本3.6:添加了
domain
属性domain
-
内存块的地址空间(
int
)。只读属性.tracemalloc使用域
0
来跟踪由Python做出的内存分配。C扩展可以使用其他域来跟踪其他资源.
size
-
内存块的大小以字节为单位(
int
).
traceback
-
在内存块分配的地方,
Traceback
instance.
Traceback
- class
tracemalloc.
Traceback
-
Frame
实例排序从最旧的帧到最近的帧.追溯至少包含
1
框架如果tracemalloc
模块失败得到一个框架,文件名"<unknown>"
在行号0
被使用.拍摄快照时,痕迹追溯仅限于
get_traceback_limit()
帧。见take_snapshot()
功能。Trace.traceback
attribute是Traceback
instance.更改版本3.7:帧现在从最旧到最近排序,而不是从最近到最旧.
format
(limit=None, most_recent_first=False)-
将回溯格式化为带换行符的行列表。使用
linecache
从源代码中检索行的模块。如果设置了limit,如果limit为正,则格式化limit最近的帧。否则,格式化abs(limit)
最旧的帧。如果most_recent_first是True
,格式化帧的顺序颠倒过来,首先返回最近的帧而不是最后一帧.与
traceback.format_tb()
函数类似,但format()
不包括换行符.例:
print("Traceback (most recent call first):")for line in traceback: print(line)
输出:
Traceback (most recent call first): File "test.py", line 9 obj = Object() File "test.py", line 12 tb = tracemalloc.get_object_traceback(f())