27.7. tracemalloc
- 跟踪内存分配¶
版本3.4中的新功能。
源代码: Lib / tracemalloc.py
tracemalloc模块是一个跟踪由Python分配的内存块的调试工具。它提供以下信息:
- 跟踪分配对象的位置
- 每个文件名和每行编号分配的内存块的统计信息:已分配内存块的总大小,数量和平均大小
- 计算两个快照之间的差异以检测内存泄漏
To trace most memory blocks allocated by Python, the module should be started as early as possible by setting the PYTHONTRACEMALLOC
environment variable to 1
, or by using -X
tracemalloc
command line option. 可以在运行时调用tracemalloc.start()
函数以开始跟踪Python内存分配。
默认情况下,分配的内存块的跟踪只存储最近的帧(1帧)。要在启动时存储25帧:将 PYTHONTRACEMALLOC
环境变量设置为25
,或使用-X
tracemalloc=25
命令行选项。
27.7.1. Examples¶
27.7.1.1. Display the top 10¶
显示分配最多内存的10个文件:
import tracemalloc
tracemalloc.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从模块加载了4.8 MiB
数据(字节码和常量),并且分配了collections
模块244 KiB
建立namedtuple
类型。
有关更多选项,请参见Snapshot.statistics()
。
27.7.1.2. Compute differences¶
拍摄两张快照并显示差异:
import tracemalloc
tracemalloc.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已经加载了模块数据(字节码和常量)的8.2 MiB
,这是4.4 MiB
超过在测试之前加载的时间。Similarly, the linecache
module has cached 940 KiB
of Python source code to format tracebacks, all of it since the previous snapshot.
如果系统具有很少的可用内存,可以使用Snapshot.dump()
方法将快照写入磁盘,以便离线分析快照。然后使用Snapshot.load()
方法重新载入快照。
27.7.1.3. Get the traceback of a memory block¶
显示最大内存块回溯的代码:
import tracemalloc
# Store 25 frames
tracemalloc.start(25)
# ... run your application ...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('traceback')
# pick the biggest memory block
stat = 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
模块中分配了最多的内存,以从模块加载数据(字节码和常量):870 KiB
。The traceback is where the importlib
loaded data most recently: on the import pdb
line of the doctest
module. 如果加载了新模块,回溯可能会更改。
27.7.1.4. Pretty top¶
忽略&lt; frozen importlib._bootstrap&gt;
和<unknown>
文件:
import linecache
import os
import tracemalloc
def display_top(snapshot, group_by='lineno', limit=10):
snapshot = snapshot.filter_traces((
tracemalloc.Filter(False, "<frozen importlib._bootstrap>"),
tracemalloc.Filter(False, "<unknown>"),
))
top_stats = snapshot.statistics(group_by)
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 KiB
Total allocated size: 5303.1 KiB
有关更多选项,请参见Snapshot.statistics()
。
27.7.2. API¶
27.7.2.1. Functions¶
-
tracemalloc.
get_object_traceback
(obj)¶ 获取分配了Python对象obj的traceback。如果
tracemalloc
模块未跟踪内存分配或未跟踪对象的分配,则返回Traceback
实例或None
。
-
tracemalloc.
get_traceback_limit
()¶ 获取跟踪的回溯中存储的最大帧数。
tracemalloc
模块必须跟踪内存分配才能获得限制,否则会引发异常。该限制由
start()
函数设置。
-
tracemalloc.
get_traced_memory
()¶ 以
tracemalloc
模块作为元组获取由其跟踪的内存块的当前大小和峰值大小:(current: int, peak: int)
。
-
tracemalloc.
get_tracemalloc_memory
()¶ 获取用于存储内存块的跟踪的
tracemalloc
模块的内存使用情况(以字节为单位)。返回int
。
-
tracemalloc.
is_tracing
()¶ True
如果tracemalloc
模块正在跟踪Python内存分配,则False
。
-
tracemalloc.
start
(nframe: int=1)¶ 开始跟踪Python内存分配:在Python内存分配器上安装钩子。收集的跟踪回溯将限制为nframe帧。默认情况下,内存块的跟踪仅存储最近的帧:限制为
1
。 nframe必须大于或等于1
。Storing more than
1
frame is only useful to compute statistics grouped by'traceback'
or to compute cumulative statistics: see theSnapshot.compare_to()
andSnapshot.statistics()
methods.存储更多帧会增加
tracemalloc
模块的内存和CPU开销。使用get_tracemalloc_memory()
函数来测量tracemalloc
模块使用的内存量。The
PYTHONTRACEMALLOC
environment variable (PYTHONTRACEMALLOC=NFRAME
) and the-X
tracemalloc=NFRAME
command line option can be used to start tracing at startup.
-
tracemalloc.
stop
()¶ 停止跟踪Python内存分配:卸载Python内存分配器上的钩子。还清除所有以前收集的由Python分配的内存块的跟踪。
调用
take_snapshot()
函数在清除之前对轨迹进行快照。另请参见
start()
,is_tracing()
和clear_traces()
函数。
-
tracemalloc.
take_snapshot
()¶ 对由Python分配的内存块的痕迹进行快照。返回新的
Snapshot
实例。快照不包括在
tracemalloc
模块开始跟踪内存分配之前分配的内存块。跟踪的回溯限制为
get_traceback_limit()
帧。使用start()
函数的nframe参数存储更多帧。tracemalloc
模块必须跟踪内存分配以拍摄快照,请参阅start()
函数。另请参见
get_object_traceback()
函数。
27.7.2.2. Filter¶
- class
tracemalloc.
Filter
(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False)¶ 在内存块的跟踪上进行过滤。
有关filename_pattern的语法,请参见
fnmatch.fnmatch()
函数。'.pyc'
文件扩展名替换为'.py'
。例子:
过滤器(True, 子过程.__文件__)
仅包含subprocess
筛选(False, tracemalloc .__ file __)
排除tracemalloc
过滤(False, “&lt;未知&gt;”)
在3.5版本中更改:
'.pyo'
文件扩展名不再替换为'.py'
。-
inclusive
¶ If inclusive is
True
(include), only trace memory blocks allocated in a file with a name matchingfilename_pattern
at line numberlineno
.If inclusive is
False
(exclude), ignore memory blocks allocated in a file with a name matchingfilename_pattern
at line numberlineno
.
-
lineno
¶ 过滤器的行号(
int
)。如果lineno为None
,则过滤器将匹配任何行号。
-
filename_pattern
¶ 过滤器的文件名模式(
str
)。
-
all_frames
¶ 如果all_frames为
True
,则检查回溯的所有帧。如果all_frames为False
,则只检查最近的帧。如果回溯限制为
1
,则此属性不起作用。请参阅get_traceback_limit()
函数和Snapshot.traceback_limit
属性。
27.7.2.3. Frame¶
27.7.2.4. Snapshot¶
- class
tracemalloc.
Snapshot
¶ 由Python分配的内存块的跟踪的快照。
take_snapshot()
函数创建快照实例。-
compare_to
(old_snapshot: Snapshot, group_by: str, cumulative: bool=False)¶ 计算与旧快照的差异。将统计信息作为按group_by分组的
StatisticDiff
实例的排序列表。请参阅group_by和累积参数的
Snapshot.statistics()
方法。结果通过以下方法从最大到最小排序:
StatisticDiff.size_diff
,StatisticDiff.size
的绝对值,StatisticDiff.count_diff
,Statistic.count
,然后按StatisticDiff.traceback
。
-
filter_traces
(filters)¶ 使用过滤的
traces
序列创建新的Snapshot
实例,过滤器是Filter
实例的列表。如果过滤器是空列表,则返回一个带有跟踪副本的新的Snapshot
实例。同时应用所有包含的过滤器,如果没有包含的过滤器匹配,则忽略跟踪。如果至少有一个排他过滤器匹配,则会忽略跟踪。
-
statistics
(group_by: str, cumulative: bool=False)¶ 将统计信息作为按group_by分组的
Statistic
实例的排序列表:通过...分组 描述 'filename'
文件名 'lineno'
文件名和行号 'traceback'
追溯 如果cumulative是
True
,则累加跟踪的回溯的所有帧的内存块的大小和计数,而不仅是最近的帧。累积模式只能与group_by等于'filename'
和'lineno'
一起使用。结果通过以下方式从最大到最小排序:
Statistic.size
,Statistic.count
,然后按Statistic.traceback
。
-
traceback_limit
¶ 存储在
traces
:拍摄快照时get_traceback_limit()
的结果。
-
traces
¶ Python分配的所有内存块的跟踪:
Trace
实例的序列。序列具有未定义的顺序。使用
Snapshot.statistics()
方法获取统计信息的排序列表。
-
27.7.2.5. Statistic¶
- class
tracemalloc.
Statistic
¶ 内存分配统计。
Snapshot.statistics()
返回Statistic
实例的列表。另请参见
StatisticDiff
类。-
count
¶ 内存块数(
int
)。
-
size
¶ 内存块的总大小(以字节为单位)(
int
)。
-
27.7.2.6. StatisticDiff¶
- class
tracemalloc.
StatisticDiff
¶ 旧的和新的
Snapshot
实例之间的内存分配上的统计差异。Snapshot.compare_to()
返回StatisticDiff
实例的列表。另请参见Statistic
类。-
count
¶ 新快照中的内存块数(
int
):0
如果内存块已在新快照中释放。
-
count_diff
¶ 如果在新快照中分配了内存块,则旧快照和新快照(
int
)之间的内存块数的差异:0
-
size
¶ 如果内存块已在新快照中释放,则新快照(
int
)中内存块的总大小(以字节为单位):0
-
size_diff
¶ 如果内存块已在新快照中分配,则旧快照和新快照(
int
)之间的内存块总大小(以字节为单位)的差异:0
-
27.7.2.7. Trace¶
27.7.2.8. Traceback¶
- class
tracemalloc.
Traceback
¶ 从最近帧到最旧帧排序的
Frame
的序列。回溯至少包含
1
帧。如果tracemalloc
模块未能获取帧,则使用行号0
的文件名"<unknown>"
。捕获快照时,跟踪的回溯限制为
get_traceback_limit()
帧。请参阅take_snapshot()
函数。Trace.traceback
属性是Traceback
实例的实例。-
format
(limit=None)¶ 将回溯格式设置为带换行符的行的列表。使用
linecache
模块从源代码中检索行。如果设置limit,则仅格式化限制最近的帧。类似于
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())
-