On this page
tracemalloc-跟踪内存分配
3.4 版的新Function。
源代码: Lib/tracemalloc.py
tracemalloc 模块是一个调试工具,用于跟踪 Python 分配的内存块。它提供以下信息:
分配对象的回溯
每个文件名和每行号分配的内存块的统计信息:分配的内存块的总大小,数量和平均大小
计算两个快照之间的差异以检测内存泄漏
要跟踪 Python 分配的大多数内存块,应pass将 PYTHONTRACEMALLOC环境变量设置为1
或使用-X tracemalloc
命令行选项来尽早启动模块。可以在运行时调用tracemalloc.start()函数以开始跟踪 Python 内存分配。
默认情况下,分配的存储块的跟踪仅存储最近的帧(1 帧)。要在启动时存储 25 个帧:将 PYTHONTRACEMALLOC环境变量设置为25
,或使用-X tracemalloc=25
命令行选项。
Examples
显示前 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 从模块中加载了4855 KiB
数据(字节码和常量),并且collections模块分配了244 KiB
来构建namedtuple类型。
有关更多选项,请参见Snapshot.statistics()。
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 已加载8173 KiB
的模块数据(字节码和常量),并且比获取前快照时测试之前加载的4428 KiB
多。类似地,linecache模块已缓存940 KiB
的 Python 源代码以格式化回溯,所有这些均自上次快照以来。
如果系统的可用内存很少,则可以使用Snapshot.dump()方法将快照写入磁盘,以离线分析快照。然后使用Snapshot.load()方法重新加载快照。
获取内存块的 traceback
显示最大内存块的回溯的代码:
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.1 KiB
模块加载数据(字节码和常量)。traceback 是importlib最近加载数据的位置:在doctest模块的import pdb
行上。如果加载了新模块,则回溯可能会更改。
Pretty top
显示 10 行的代码,分配最多的内存,输出漂亮的代码,忽略<frozen importlib._bootstrap>
和<unknown>
文件:
import linecache
import os
import tracemalloc
def 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]
print("#%s: %s:%s: %.1f KiB"
% (index, frame.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()。
API
Functions
tracemalloc.
clear_traces
( )- 清除由 Python 分配的内存块的痕迹。
另请参见stop()。
tracemalloc.
get_object_traceback
(* obj *)- 获取分配 Python 对象* obj *的位置的 traceback。返回一个Traceback实例,如果tracemalloc模块未跟踪内存分配或未跟踪对象的分配,则返回
None
。
- 获取分配 Python 对象* obj *的位置的 traceback。返回一个Traceback实例,如果tracemalloc模块未跟踪内存分配或未跟踪对象的分配,则返回
另请参见gc.get_referrers()和sys.getsizeof()函数。
tracemalloc.
get_traceback_limit
( )- 获取在跟踪的回溯中存储的最大帧数。
tracemalloc模块必须跟踪内存分配以获取限制,否则会引发异常。
该限制由start()Function设置。
tracemalloc.
get_traced_memory
( )- 获取由tracemalloc模块作为 Tuples
(current: int, peak: int)
跟踪的内存块的当前大小和峰值大小。
- 获取由tracemalloc模块作为 Tuples
tracemalloc.
get_tracemalloc_memory
( )- 获取用于存储内存块跟踪的tracemalloc模块的内存使用情况(以字节为单位)。返回int。
tracemalloc.
is_tracing
( )True
(如果tracemalloc模块正在跟踪 Python 内存分配),否则False
。
tracemalloc.
start
(* nframe:int = 1 *)- 开始跟踪 Python 内存分配:在 Python 内存分配器上安装钩子。收集的跟踪回溯将仅限于* nframe *帧。默认情况下,内存块的跟踪仅存储最近的帧:限制为
1
。 * nframe *必须大于或等于1
。
- 开始跟踪 Python 内存分配:在 Python 内存分配器上安装钩子。收集的跟踪回溯将仅限于* nframe *帧。默认情况下,内存块的跟踪仅存储最近的帧:限制为
存储多于1
帧仅对计算按'traceback'
分组的统计信息或计算累积统计信息有用:请参阅Snapshot.compare_to()和Snapshot.statistics()方法。
存储更多帧会增加tracemalloc模块的内存和 CPU 开销。使用get_tracemalloc_memory()函数来衡量tracemalloc模块使用了多少内存。
PYTHONTRACEMALLOC环境变量(PYTHONTRACEMALLOC=NFRAME
)和-X tracemalloc=NFRAME
命令行选项可用于在启动时开始跟踪。
另请参见stop(),is_tracing()和get_traceback_limit()函数。
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()函数。
DomainFilter
-
- class *
tracemalloc.
DomainFilter
(*包含:bool , domain:int *)
- 按内存块的地址空间(域)过滤痕迹。
- class *
3.6 版的新Function。
inclusive
- 如果* inclusive *为
True
(包括),则匹配在地址空间domain中分配的存储块。
- 如果* inclusive *为
如果* inclusive *为False
(排除),则匹配未在地址空间domain中分配的存储块。
domain
- 存储块(
int
)的地址空间。只读属性。
- 存储块(
Filter
-
- class *
tracemalloc.
Filter
(*包括:bool , filename_pattern:str , lineno:int = None , all_frames:bool = False , domain:int = None *)
- 过滤存储块的痕迹。
- class *
有关* filename_pattern *的语法,请参见fnmatch.fnmatch()函数。 '.pyc'
文件 extensions 替换为'.py'
。
Examples:
Filter(True, subprocess.__file__)
仅包含subprocess模块的跟踪Filter(False, tracemalloc.__file__)
排除tracemalloc模块的痕迹Filter(False, "<unknown>")
排除空的回溯
在版本 3.5 中更改:'.pyo'
文件 extensions 不再由'.py'
代替。
在版本 3.6 中更改:添加了domain属性。
domain
- 内存块的地址空间(
int
或None
)。
- 内存块的地址空间(
tracemalloc 使用域0
来跟踪 Python 进行的内存分配。 C 扩展可以使用其他域来跟踪其他资源。
inclusive
- 如果* inclusive *为
True
(包括),则仅匹配名称在行号lineno上匹配filename_pattern的文件中分配的存储块。
- 如果* inclusive *为
如果* inclusive *为False
(排除),则忽略在文件名中与行号lineno匹配filename_pattern的文件中分配的存储块。
lineno
- 过滤器的行号(
int
)。如果* lineno *为None
,则过滤器匹配任何行号。
- 过滤器的行号(
filename_pattern
- 过滤器的文件名模式(
str
)。只读属性。
- 过滤器的文件名模式(
all_frames
- 如果* all_frames 为
True
,则会检查回溯的所有帧。如果 all_frames *为False
,则仅检查最近的帧。
- 如果* all_frames 为
如果回溯限制为1
,则此属性无效。请参见get_traceback_limit()函数和Snapshot.traceback_limit属性。
Frame
- 类别
tracemalloc.
Frame
- traceback 的框架。
filename
- 文件名(
str
)。
- 文件名(
lineno
- 行号(
int
)。
- 行号(
Snapshot
- 类别
tracemalloc.
Snapshot
- Python 分配的内存块的痕迹快照。
take_snapshot()函数创建一个快照实例。
compare_to
(* old_snapshot:Snapshot , key_type:str , cumulative:bool = False *)- 使用旧快照计算差异。获取统计信息,作为按* key_type *分组的StatisticDiff个实例的排序列表。
请参阅Snapshot.statistics()方法以获取* key_type 和 cumulative *参数。
结果按从最大到最小的 Sequences 排序:绝对值StatisticDiff.size_diff,StatisticDiff.size,绝对值StatisticDiff.count_diff,Statistic.count然后是StatisticDiff.traceback。
dump
(文件名)- 将快照写入文件。
使用load()重新加载快照。
filter_traces
(过滤器)- 使用过滤的traces序列创建一个新的Snapshot实例,* filters 是DomainFilter和Filter实例的列表。如果 filters *是一个空列表,则返回一个新的Snapshot实例,并带有跟踪副本。
一次性应用所有包含过滤器,如果没有包含过滤器匹配该跟踪,则忽略该跟踪。如果至少有一个专用过滤器与它匹配,则忽略该跟踪。
在版本 3.6 中更改:现在,过滤器也接受了DomainFilter个实例。
- 类方法
load
(文件名)- 从文件加载快照。
另请参见dump()。
statistics
(* key_type:str , cumulative:bool = False *)- 获取统计信息,作为按_key_type *分组的Statistic个实例的排序列表:
key_type | description |
---|---|
'filename' |
filename |
'lineno' |
文件名和行号 |
'traceback' |
traceback |
如果* cumulative 为True
,则累计跟踪的回溯所有帧(不仅是最近的帧)的大小和存储块数。累积模式只能与 key_type *等于'filename'
和'lineno'
一起使用。
结果按大小从大到小排序:Statistic.size,Statistic.count然后是Statistic.traceback。
traceback_limit
- traces的回溯中存储的最大帧数:拍摄快照时get_traceback_limit()的结果。
traces
- Python 分配的所有内存块的跟踪:Trace个实例的序列。
序列具有不确定的 Sequences。使用Snapshot.statistics()方法可获取统计信息的排序列表。
Statistic
- 类别
tracemalloc.
Statistic
- 内存分配的统计信息。
Snapshot.statistics()返回Statistic实例的列表。
另请参见StatisticDiff类。
count
- 内存块数(
int
)。
- 内存块数(
size
- 内存块的总大小,以字节为单位(
int
)。
- 内存块的总大小,以字节为单位(
traceback
- 分配内存块的位置Traceback实例的回溯。
StatisticDiff
- 类别
tracemalloc.
StatisticDiff
- 新旧Snapshot实例之间的内存分配统计差异。
Snapshot.compare_to()返回StatisticDiff实例的列表。另请参见Statistic类。
count
- 新快照中的内存块数(
int
):0
(如果已在新快照中释放了内存块)。
- 新快照中的内存块数(
count_diff
- 新快照和旧快照之间的内存块数量差异(
int
):0
(如果已在新快照中分配了内存块)。
- 新快照和旧快照之间的内存块数量差异(
size
- 新快照中的存储块总大小(以字节为单位)(
int
):0
(如果已在新快照中释放存储块)。
- 新快照中的存储块总大小(以字节为单位)(
size_diff
- 旧快照和新快照之间的内存块总大小(以字节为单位)之间的差异(
int
):0
(如果已在新快照中分配了内存块)。
- 旧快照和新快照之间的内存块总大小(以字节为单位)之间的差异(
traceback
- traceback 到分配了内存块的位置,Traceback实例。
Trace
- 类别
tracemalloc.
Trace
- 内存块的跟踪。
Snapshot.traces属性是Trace个实例的序列。
在版本 3.6 中更改:添加了domain属性。
domain
- 存储块(
int
)的地址空间。只读属性。
- 存储块(
tracemalloc 使用域0
来跟踪 Python 进行的内存分配。 C 扩展可以使用其他域来跟踪其他资源。
size
- 内存块的大小,以字节为单位(
int
)。
- 内存块的大小,以字节为单位(
traceback
- 分配内存块的位置Traceback实例的回溯。
Traceback
- 类别
tracemalloc.
Traceback
- Frame个实例的 Sequences 从最旧的帧到最新的帧排序。
traceback 至少包含1
帧。如果tracemalloc
模块无法获取帧,则使用行号0
的文件名"<unknown>"
。
拍摄快照时,跟踪的回溯限制为get_traceback_limit()帧。请参见take_snapshot()函数。
Trace.traceback属性是Traceback实例的实例。
在 3.7 版中进行了更改:现在将帧从最旧到最新,而不是从最新到最旧排序。
format
(* limit = None , most_recent_first = False *)- 将回溯格式设置为带有换行符的行列表。使用linecache模块从源代码中检索行。如果设置了* limit ,则当 limit 为正数时,格式化 limit 最近的帧。否则,请格式化
abs(limit)
最旧的帧。如果 most_recent_first *为True
,则格式化帧的 Sequences 相反,首先返回最新的帧,而不是最后返回的帧。
- 将回溯格式设置为带有换行符的行列表。使用linecache模块从源代码中检索行。如果设置了* limit ,则当 limit 为正数时,格式化 limit 最近的帧。否则,请格式化
与traceback.format_tb()函数相似,不同之处在于format()不包含换行符。
Example:
print("Traceback (most recent call first):")
for line in traceback:
print(line)
Output:
Traceback (most recent call first):
File "test.py", line 9
obj = Object()
File "test.py", line 12
tb = tracemalloc.get_object_traceback(f())