tracemalloc
--- メモリ割り当ての追跡¶
Added in version 3.4.
ソースコード: Lib/tracemalloc.py
tracemallocモジュールは、Python が割り当てたメモリブロックをトレースするためのデバッグツールです。このモジュールは以下の情報を提供します。
オブジェクトが割り当てられた場所のトレースバック
ファイル名ごと、及び行ごとに割り当てられたメモリブロックの以下の統計を取ります:総サイズ、ブロック数、割り当てられたブロックの平均サイズ
メモリリークを検出するために2つのスナップショットの差を計算します。
Python が割り当てたメモリブロックの大半をトレースするには、 PYTHONTRACEMALLOC
環境変数を 1
に設定して可能な限り早くモジュールを開始させるか、-X
tracemalloc
コマンドラインオプションを使用してください。実行時に tracemalloc.start()
を呼んで Python のメモリ割り当てのトレースを開始することが出来ます。
デフォルトでは、割り当てられたメモリブロック1つのトレースは最新1フレームを保存します。開始時に25フレームを保存するには、PYTHONTRACEMALLOC
環境変数を 25
に設定するか、-X
tracemalloc=25
コマンドラインオプションを使用してください。
使用例¶
上位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
モジュールが namedtuple
型をビルドするのに 244 KiB
を割り当てていることが分かります。
オプションの詳細については Snapshot.statistics()
を参照してください。
差を計算する¶
スナップショットを2つ取り、差を表示します:
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
モジュールはトレースバックの書式化に Python ソースコード 940 KiB
をキャッシュし、その全ては前回のスナップショットの後に行われたことが分かります。
システムに空きメモリがほとんどない場合、スナップショットをオフラインで解析するための Snapshot.dump()
メソッドを使用して、スナップショットをディスクに書き込むことが出来ます。
そして Snapshot.load()
メソッドを使用してスナップショットを再読み込みします。
メモリブロックのトレースバックを取得する¶
最大のメモリブロックのトレースバックを表示するコードです:
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)
We can see that the most memory was allocated in the importlib
module to
load data (bytecode and constants) from modules: 870.1 KiB
. The traceback is
where the importlib
loaded data most recently: on the import pdb
line of the doctest
module. The traceback may change if a new module is
loaded.
top を整形化する¶
<frozen importlib._bootstrap>
および <unknown>
ファイルを無視して、
メモリ割り当て量の上位10を整形化して表示するコードです:
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()
を参照してください。
Record the current and peak size of all traced memory blocks¶
The following code computes two sums like 0 + 1 + 2 + ...
inefficiently, by
creating a list of those numbers. This list consumes a lot of memory
temporarily. We can use get_traced_memory()
and reset_peak()
to
observe the small memory usage after the sum is computed as well as the peak
memory usage during the computations:
import tracemalloc
tracemalloc.start()
# Example code: compute a sum with a large temporary list
large_sum = sum(list(range(100000)))
first_size, first_peak = tracemalloc.get_traced_memory()
tracemalloc.reset_peak()
# Example code: compute a sum with a small temporary list
small_sum = sum(list(range(1000)))
second_size, second_peak = tracemalloc.get_traced_memory()
print(f"{first_size=}, {first_peak=}")
print(f"{second_size=}, {second_peak=}")
出力:
first_size=664, first_peak=3592984
second_size=804, second_peak=29704
Using reset_peak()
ensured we could accurately record the peak during the
computation of small_sum
, even though it is much smaller than the overall
peak size of memory blocks since the start()
call. Without the call to
reset_peak()
, second_peak
would still be the peak from the
computation large_sum
(that is, equal to first_peak
). In this case,
both peaks are much higher than the final memory usage, and which suggests we
could optimise (by removing the unnecessary call to list
, and writing
sum(range(...))
).
API¶
関数¶
- tracemalloc.get_object_traceback(obj)¶
Python オブジェクト obj が割り当てられたトレースバックを取得します。
Traceback
インスタンスか、tracemalloc
モジュールがメモリ割り当てをトレースしていない場合かオブジェクトの割り当てをトレースしていない場合は、None
を返します。gc.get_referrers()
やsys.getsizeof()
関数も参照してください。
- tracemalloc.get_traceback_limit()¶
トレースのトレースバック内に格納されている最大フレーム数を取得します。
tracemalloc
モジュールは上限を取得するためにメモリ割り当てをトレースしていなければなりません。そうでなければ例外が送出されます。start()
関数で上限を設定します。
- tracemalloc.get_traced_memory()¶
tracemalloc
モジュールがトレースするメモリブロックの現在のサイズと最大時のサイズをタプルとして取得します:(current: int, peak: int)
。
- tracemalloc.reset_peak()¶
tracemalloc
モジュールがトレースするメモリブロックの最大時のサイズを現在のサイズに設定します。Do nothing if the
tracemalloc
module is not tracing memory allocations.This function only modifies the recorded peak size, and does not modify or clear any traces, unlike
clear_traces()
. Snapshots taken withtake_snapshot()
before a call toreset_peak()
can be meaningfully compared to snapshots taken after the call.get_traced_memory()
も参照してください。Added in version 3.9.
- tracemalloc.get_tracemalloc_memory()¶
tracemalloc
モジュールがメモリブロックのトレースを保存するのに使用しているメモリ使用量をバイト単位で取得します。int
を返します。
- tracemalloc.is_tracing()¶
tracemalloc
モジュールが Python のメモリ割り当てをトレースしていればTrue
を、そうでなければFalse
を返します。
- tracemalloc.start(nframe: int = 1)¶
Python のメモリ割り当てのトレースを開始します: Python メモリアロケータにフックします。 トレースの収集されたトレースバックは nframe フレームに制限されます。 デフォルトでは、あるブロックのトレースは最新のフレームのみを保存します、つまり上限は
1
です。 nframe は1
以上でなければなりません。You can still read the original number of total frames that composed the traceback by looking at the
Traceback.total_nframe
attribute.1
より多くのフレームを保存するのは'traceback'
でグループ化された統計や累積的な統計を計算する場合にのみ有用です。Snapshot.compare_to()
およびSnapshot.statistics()
メソッドを参照してください。保存するフレーム数を増やすと
tracemalloc
モジュールのメモリと CPU のオーバーヘッドは増加します。tracemalloc
モジュールが使用しているメモリ量を調べるにはget_tracemalloc_memory()
関数を使用してください。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(inclusive: bool, domain: int)¶
Filter traces of memory blocks by their address space (domain).
Added in version 3.6.
- inclusive¶
If inclusive is
True
(include), match memory blocks allocated in the address spacedomain
.If inclusive is
False
(exclude), match memory blocks not allocated in the address spacedomain
.
- domain¶
Address space of a memory block (
int
). Read-only property.
Filter¶
- class tracemalloc.Filter(inclusive: bool, filename_pattern: str, lineno: int = None, all_frames: bool = False, domain: int = None)¶
メモリブロックのトレースをフィルターします。
filename_pattern のシンタックスについては
fnmatch.fnmatch()
関数を参照してください。'.pyc'
拡張子は'.py'
に置換されます。例:
Filter(True, subprocess.__file__)
はsubprocess
モジュールのみを含みますFilter(False, tracemalloc.__file__)
はtracemalloc
モジュールのトレースを除外しますFilter(False, "<unknown>")
は空のトレースバックを除外します
バージョン 3.5 で変更:
'.pyo'
ファイル拡張子が'.py'
に置換されることはもうありません。バージョン 3.6 で変更:
domain
属性が追加されました。- domain¶
Address space of a memory block (
int
orNone
).tracemalloc uses the domain
0
to trace memory allocations made by Python. C extensions can use other domains to trace other resources.
- inclusive¶
If inclusive is
True
(include), only match 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
属性を参照してください。
Frame¶
Snapshot¶
- class tracemalloc.Snapshot¶
Python が割り当てたメモリブロックのトレースのスナップショットです。
take_snapshot()
関数はスナップショットのインスタンスを作ります。- compare_to(old_snapshot: Snapshot, key_type: str, cumulative: bool = False)¶
古いスナップショットとの差を計算します。 key_type でグループ化された
StatisticDiff
インスタンスのソート済みリストとして統計を取得します。key_type および cumulative 引数については
Snapshot.statistics()
メソッドを参照してください。結果は降順でソートされます: キーは
StatisticDiff.size_diff
の絶対値、StatisticDiff.size
、StatisticDiff.count_diff
の絶対置、Statistic.count
、そしてStatisticDiff.traceback
です。
- filter_traces(filters)¶
Create a new
Snapshot
instance with a filteredtraces
sequence, filters is a list ofDomainFilter
andFilter
instances. If filters is an empty list, return a newSnapshot
instance with a copy of the traces.All inclusive filters are applied at once, a trace is ignored if no inclusive filters match it. A trace is ignored if at least one exclusive filter matches it.
バージョン 3.6 で変更:
DomainFilter
instances are now also accepted in filters.
- statistics(key_type: str, cumulative: bool = False)¶
key_type でグループ化された
Statistic
インスタンスのソート済みリストとして統計を取得します:key_type
description
'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
インスタンスのシークェンスです。シークェンスの順序は未定義です。統計のソート済みリストを取得するには
Snapshot.statistics()
を使用してください。
Statistic¶
- class tracemalloc.Statistic¶
メモリ割り当ての統計です。
Snapshot.statistics()
はStatistic
インスタンスの一覧を返します。StatisticDiff
クラスも参照してください。- count¶
メモリブロック数 (
int
)。
- size¶
メモリブロックのバイト単位の総サイズ (
int
)。
StatisticDiff¶
- class tracemalloc.StatisticDiff¶
新旧
Snapshot
インスタンスのメモリ割り当ての統計差です。Snapshot.compare_to()
はStatisticDiff
インスタンスのリストを返します。Statistic
クラスも参照してください。- count¶
新しいスナップショット内のメモリブロックの数 (
int
) です。新しいスナップショット内でメモリブロックが解放された場合は0
です。
- count_diff¶
新旧スナップショットのメモリブロック数の差 (
int
) です。メモリブロックが新しいスナップショット内で割り当てられた場合は0
です。
- size¶
新しいスナップショット内のメモリブロックのバイト単位での総サイズ (
int
) です。新しいスナップショット内でメモリブロックが解放された場合は0
です。
- size_diff¶
新旧スナップショットのバイト単位での総サイズの差 (
int
) です。メモリブロックが新しいスナップショット内で割り当てられた場合は0
です。
Trace¶
- class tracemalloc.Trace¶
メモリブロックをトレースします。
Snapshot.traces
属性はTrace
インスタンスのシークエンスです。バージョン 3.6 で変更:
domain
属性が追加されました。- domain¶
Address space of a memory block (
int
). Read-only property.tracemalloc uses the domain
0
to trace memory allocations made by Python. C extensions can use other domains to trace other resources.
- size¶
メモリブロックのバイト単位のサイズ (
int
)。
Traceback¶
- class tracemalloc.Traceback¶
Sequence of
Frame
instances sorted from the oldest frame to the most recent frame.A traceback contains at least
1
frame. If thetracemalloc
module failed to get a frame, the filename"<unknown>"
at line number0
is used.When a snapshot is taken, tracebacks of traces are limited to
get_traceback_limit()
frames. See thetake_snapshot()
function. The original number of frames of the traceback is stored in theTraceback.total_nframe
attribute. That allows to know if a traceback has been truncated by the traceback limit.Trace.traceback
属性はTraceback
のインスタンスです。バージョン 3.7 で変更: Frames are now sorted from the oldest to the most recent, instead of most recent to oldest.
- total_nframe¶
Total number of frames that composed the traceback before truncation. This attribute can be set to
None
if the information is not available.
バージョン 3.9 で変更:
Traceback.total_nframe
属性が追加されました。- format(limit=None, most_recent_first=False)¶
Format the traceback as a list of lines. Use the
linecache
module to retrieve lines from the source code. If limit is set, format the limit most recent frames if limit is positive. Otherwise, format theabs(limit)
oldest frames. If most_recent_first isTrue
, the order of the formatted frames is reversed, returning the most recent frame first instead of last.Similar to the
traceback.format_tb()
function, except thatformat()
does not include newlines.以下はプログラム例です:
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())