27.7. tracemalloc --- メモリ割り当ての追跡¶
バージョン 3.4 で追加.
ソースコード: Lib/tracemalloc.py
tracemallocモジュールは、Python が割り当てたメモリブロックをトレースするためのデバッグツールです。このモジュールは以下の情報を提供します。
オブジェクトが割り当てられた場所のトレースバック
ファイル名ごと、及び行ごとに割り当てられたメモリブロックの以下の統計を取ります:総サイズ、ブロック数、割り当てられたブロックの平均サイズ
メモリリークを検出するために2つのスナップショットの差を計算します。
Python が割り当てたメモリブロックの大半をトレースするには、 PYTHONTRACEMALLOC 環境変数を 1 に設定して可能な限り早くモジュールを開始させるか、-X tracemalloc コマンドラインオプションを使用してください。実行時に tracemalloc.start() を呼んで Python のメモリ割り当てのトレースを開始することが出来ます。
デフォルトでは、割り当てられたメモリブロック1つのトレースは最新1フレームを保存します。開始時に25フレームを保存するには、PYTHONTRACEMALLOC 環境変数を 25 に設定するか、-X tracemalloc=25 コマンドラインオプションを使用してください。
27.7.1. 使用例¶
27.7.1.1. 上位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() を参照してください。
27.7.1.2. 差を計算する¶
スナップショットを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() メソッドを使用してスナップショットを再読み込みします。
27.7.1.3. メモリブロックのトレースバックを取得する¶
最大のメモリブロックのトレースバックを表示するコードです:
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.
27.7.1.4. 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]
# 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. 関数¶
-
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.get_tracemalloc_memory()¶ tracemallocモジュールがメモリブロックのトレースを保存するのに使用しているメモリ使用量をバイト単位で取得します。intを返します。
-
tracemalloc.is_tracing()¶ tracemallocモジュールが Python のメモリ割り当てをトレースしていればTrueを、そうでなければFalseを返します。
-
tracemalloc.start(nframe: int=1)¶ Python のメモリ割り当てのトレースを開始します: Python メモリアロケータにフックします。 トレースの収集されたトレースバックは nframe フレームに制限されます。 デフォルトでは、あるブロックのトレースは最新のフレームのみを保存します、つまり上限は
1です。 nframe は1以上でなければなりません。1より多くのフレームを保存するのは'traceback'でグループ化された統計や累積的な統計を計算する場合にのみ有用です。Snapshot.compare_to()およびSnapshot.statistics()メソッドを参照してください。保存するフレーム数を増やすと
tracemallocモジュールのメモリと CPU のオーバーヘッドは増加します。tracemallocモジュールが使用しているメモリ量を調べるにはget_tracemalloc_memory()関数を使用してください。PYTHONTRACEMALLOC環境変数 (PYTHONTRACEMALLOC=NFRAME) と-Xtracemalloc=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()関数を参照してください。
27.7.2.2. DomainFilter¶
-
class
tracemalloc.DomainFilter(inclusive: bool, domain: int)¶ Filter traces of memory blocks by their address space (domain).
バージョン 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.
-
27.7.2.3. 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 (
intorNone).
-
inclusive¶ If inclusive is
True(include), only match memory blocks allocated in a file with a name matchingfilename_patternat line numberlineno.If inclusive is
False(exclude), ignore memory blocks allocated in a file with a name matchingfilename_patternat 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.4. Frame¶
27.7.2.5. 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
Snapshotinstance with a filteredtracessequence, filters is a list ofDomainFilterandFilterinstances. If filters is an empty list, return a newSnapshotinstance 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 で変更:
DomainFilterinstances 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()を使用してください。
-
27.7.2.6. Statistic¶
-
class
tracemalloc.Statistic¶ メモリ割り当ての統計です。
Snapshot.statistics()はStatisticインスタンスの一覧を返します。StatisticDiffクラスも参照してください。-
count¶ メモリブロック数 (
int)。
-
size¶ メモリブロックのバイト単位の総サイズ (
int)。
-
27.7.2.7. 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.8. Trace¶
27.7.2.9. Traceback¶
-
class
tracemalloc.Traceback¶ Sequence of
Frameinstances sorted from the most recent frame to the oldest frame.A traceback contains at least
1frame. If thetracemallocmodule failed to get a frame, the filename"<unknown>"at line number0is used.When a snapshot is taken, tracebacks of traces are limited to
get_traceback_limit()frames. See thetake_snapshot()function.Trace.traceback属性はTTracebackのインスタンスです。-
format(limit=None)¶ Format the traceback as a list of lines with newlines. Use the
linecachemodule to retrieve lines from the source code. If limit is set, only format the limit most recent frames.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())
-
