tracemalloc —メモリ割り当てをトレースします
バージョン3.4の新機能。
ソースコード: :source: `Lib / tracemalloc.py`
tracemallocモジュールは、Pythonによって割り当てられたメモリブロックをトレースするためのデバッグツールです。 次の情報を提供します。
- オブジェクトが割り当てられたトレースバック
- ファイル名ごとおよび行番号ごとに割り当てられたメモリブロックの統計:割り当てられたメモリブロックの合計サイズ、数、および平均サイズ
- 2つのスナップショットの差を計算して、メモリリークを検出します
Pythonによって割り当てられたほとんどのメモリブロックをトレースするには、 PYTHONTRACEMALLOC 環境変数を1
に設定するか、を使用して、モジュールをできるだけ早く起動する必要があります。 -X tracemalloc
コマンドラインオプション。 tracemalloc.start()関数を実行時に呼び出して、Pythonメモリ割り当てのトレースを開始できます。
デフォルトでは、割り当てられたメモリブロックのトレースには、最新のフレーム(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 モジュールが244 KiB
を割り当てて namedtuple タイプを構築したことがわかります。
その他のオプションについては、 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)
importlib モジュールでは、モジュールからデータ(バイトコードと定数)をロードするために、ほとんどのメモリが割り当てられていることがわかります:870.1 KiB
。 トレースバックは、 importlib が最近データをロードした場所です。 doctest モジュールのimport pdb
行にあります。 新しいモジュールがロードされると、トレースバックが変更される場合があります。
かなりトップ
<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()を参照してください。
トレースされたすべてのメモリブロックの現在のサイズとピークサイズを記録します
次のコードは、0 + 1 + 2 + ...
のような2つの合計を、それらの数値のリストを作成することによって非効率的に計算します。 このリストは一時的に大量のメモリを消費します。 get_traced_memory()および reset_peak()を使用して、合計が計算された後の小さなメモリ使用量と、計算中のピークメモリ使用量を観察できます。
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
reset_peak()を使用すると、 start()以降のメモリブロックの全体的なピークサイズよりもはるかに小さい場合でも、small_sum
の計算中にピークを正確に記録できることが保証されました。 呼び出し。 reset_peak()を呼び出さなくても、second_peak
は計算large_sum
のピークになります(つまり、first_peak
に等しくなります)。 この場合、両方のピークが最終的なメモリ使用量よりもはるかに高く、最適化できることを示しています( list への不要な呼び出しを削除し、sum(range(...))
を書き込むことによって)。
API
関数
- tracemalloc.clear_traces()
Pythonによって割り当てられたメモリブロックのトレースをクリアします。
stop()も参照してください。
- 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 モジュールによってトレースされるメモリブロックのピークサイズを現在のサイズに設定します。
tracemalloc モジュールがメモリ割り当てをトレースしていない場合は、何もしません。
この関数は、 clear_traces()とは異なり、記録されたピークサイズのみを変更し、トレースを変更またはクリアしません。 reset_peak()を呼び出す前に take_snapshot()で取得したスナップショットは、呼び出し後に取得したスナップショットと有意義に比較できます。
get_traced_memory()も参照してください。
バージョン3.9の新機能。
- tracemalloc.get_tracemalloc_memory()
- メモリブロックのトレースを格納するために使用される tracemalloc モジュールのメモリ使用量をバイト単位で取得します。 int を返します。
- tracemalloc.is_tracing()
True
tracemalloc モジュールがPythonメモリ割り当てをトレースしている場合は、False
。それ以外の場合。
- tracemalloc.start(nframe: int = 1)
Pythonメモリ割り当てのトレースを開始します。Pythonメモリアロケータにフックをインストールします。 収集されるトレースのトレースバックは、 nframe フレームに制限されます。 デフォルトでは、メモリブロックのトレースは最新のフレームのみを保存します。制限は
1
です。 nframe は1
以上である必要があります。Traceback.total_nframe 属性を確認することで、トレースバックを構成した元の合計フレーム数を引き続き読み取ることができます。
1
フレームを超えて保存すると、'traceback'
でグループ化された統計を計算する場合、または累積統計を計算する場合にのみ役立ちます。 Snapshot.compare_to()および Snapshot.statisticsを参照してください。 ()メソッド。より多くのフレームを格納すると、 tracemalloc モジュールのメモリとCPUのオーバーヘッドが増加します。 get_tracemalloc_memory()関数を使用して、 tracemalloc モジュールによって使用されているメモリの量を測定します。
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()関数も参照してください。
DomainFilter
- class tracemalloc.DomainFilter(inclusive: bool, domain: int)
メモリブロックのトレースをアドレス空間(ドメイン)でフィルタリングします。
バージョン3.6の新機能。
- inclusive
inclusive が
True
(include)の場合、アドレス空間 domain に割り当てられているメモリブロックを一致させます。包括的が
False
(除外)の場合、アドレス空間ドメインに割り当てられていないメモリブロックを一致させます。
- domain
メモリブロックのアドレス空間(
int
)。 読み取り専用プロパティ。
フィルター
- 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__)
には、サブプロセスモジュールのトレースのみが含まれますFilter(False, tracemalloc.__file__)
は、 tracemalloc モジュールのトレースを除外しますFilter(False, "<unknown>")
は空のトレースバックを除外します
バージョン3.5で変更:
'.pyo'
ファイル拡張子は'.py'
に置き換えられなくなりました。バージョン3.6で変更: domain 属性が追加されました。
- domain
メモリブロックのアドレス空間(
int
またはNone
)。tracemallocは、ドメイン
0
を使用して、Pythonによって行われたメモリ割り当てをトレースします。 C拡張機能は、他のドメインを使用して他のリソースをトレースできます。
- inclusive
包括的が
True
(インクルード)の場合、行番号 lineno で filename_pattern と一致する名前のファイルに割り当てられたメモリブロックのみを照合します。包括的が
False
(除外)の場合、行番号 lineno で filename_pattern と一致する名前のファイルに割り当てられたメモリブロックを無視します。
- 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 クラスは、 Frame インスタンスのシーケンスです。
- filename
ファイル名(
str
)。
- lineno
行番号(
int
)。
スナップショット
- 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()メソッドを参照してください。
結果は、 StatisticsDiff.size_diff 、 StatisticsDiff.size の絶対値、 StatisticsDiff.count_diff 、の絶対値によって最大から最小にソートされます。 ] Statistics.count 、次に StatisticsDiff.traceback 。
- dump(filename)
スナップショットをファイルに書き込みます。
load()を使用してスナップショットをリロードします。
- filter_traces(filters)
フィルター処理されたトレースシーケンスを使用して新しいスナップショットインスタンスを作成します。フィルターはドメインフィルターおよびフィルターインスタンスのリストです。 filters が空のリストの場合は、トレースのコピーを含む新しい Snapshot インスタンスを返します。
すべての包括的フィルターが一度に適用され、それに一致する包括的フィルターがない場合、トレースは無視されます。 少なくとも1つの排他フィルターが一致する場合、トレースは無視されます。
バージョン3.6で変更: DomainFilter インスタンスが filters でも受け入れられるようになりました。
- classmethod load(filename)
ファイルからスナップショットをロードします。
dump()も参照してください。
- statistics(key_type: str, cumulative: bool = False)
key_type でグループ化された Statistic インスタンスのソートされたリストとして統計を取得します。
key_type
説明
'filename'
ファイル名
'lineno'
ファイル名と行番号
'traceback'
トレースバック
cumulative が
True
の場合、最新のフレームだけでなく、トレースのトレースバックのすべてのフレームのメモリブロックのサイズとカウントを累積します。 累積モードは、 key_type が'filename'
および'lineno'
と等しい場合にのみ使用できます。結果は、 Statistics.size 、 Statistics.count 、 Statistics.traceback の順に、大きいものから小さいものへと並べ替えられます。
- traceback_limit
traces のトレースバックに保存されるフレームの最大数:スナップショットが作成されたときの get_traceback_limit()の結果。
- traces
Pythonによって割り当てられたすべてのメモリブロックのトレース: Trace インスタンスのシーケンス。
シーケンスの順序は未定義です。 Snapshot.statistics()メソッドを使用して、統計のソートされたリストを取得します。
統計
- class tracemalloc.Statistic
メモリ割り当てに関する統計。
Snapshot.statistics()は、 Statistics インスタンスのリストを返します。
StatisticsDiff クラスも参照してください。
- count
メモリブロックの数(
int
)。
- size
バイト単位のメモリブロックの合計サイズ(
int
)。
- traceback
メモリブロックが割り当てられたトレースバック、 Traceback インスタンス。
StatisticDiff
- class tracemalloc.StatisticDiff
古い Snapshot インスタンス間のメモリ割り当ての統計的差異。
Snapshot.compare_to()は、 StatisticsDiff インスタンスのリストを返します。 Statistics クラスも参照してください。
- count
新しいスナップショットのメモリブロック数(
int
):メモリブロックが新しいスナップショットで解放されている場合は0
。
- count_diff
古いスナップショットと新しいスナップショットのメモリブロック数の違い(
int
):メモリブロックが新しいスナップショットに割り当てられている場合は0
。
- size
新しいスナップショットのメモリブロックの合計サイズ(バイト単位)(
int
):メモリブロックが新しいスナップショットで解放されている場合は0
。
- size_diff
古いスナップショットと新しいスナップショットのメモリブロックの合計サイズのバイト単位の違い(
int
):0
メモリブロックが新しいスナップショットに割り当てられている場合。
- traceback
メモリブロックが割り当てられたトレースバック、 Traceback インスタンス。
痕跡
- class tracemalloc.Trace
メモリブロックのトレース。
Snapshot.traces 属性は、 Trace インスタンスのシーケンスです。
バージョン3.6で変更: domain 属性が追加されました。
- domain
メモリブロックのアドレス空間(
int
)。 読み取り専用プロパティ。tracemallocは、ドメイン
0
を使用して、Pythonによって行われたメモリ割り当てをトレースします。 C拡張機能は、他のドメインを使用して他のリソースをトレースできます。
- size
バイト単位のメモリブロックのサイズ(
int
)。
- traceback
メモリブロックが割り当てられたトレースバック、 Traceback インスタンス。
トレースバック
- class tracemalloc.Traceback
最も古いフレームから最新のフレームにソートされた Frame インスタンスのシーケンス。
トレースバックには、少なくとも
1
フレームが含まれています。tracemalloc
モジュールがフレームの取得に失敗した場合、行番号0
のファイル名"<unknown>"
が使用されます。スナップショットが作成されると、トレースのトレースバックは get_traceback_limit()フレームに制限されます。 take_snapshot()関数を参照してください。 トレースバックの元のフレーム数は、 Traceback.total_nframe 属性に格納されます。 これにより、トレースバックがトレースバック制限によって切り捨てられたかどうかを知ることができます。
Trace.traceback 属性は、 Traceback インスタンスのインスタンスです。
バージョン3.7での変更:フレームは、最新から最も古いものではなく、最も古いものから最新のものにソートされるようになりました。
- total_nframe
切り捨て前にトレースバックを構成したフレームの総数。 情報が利用できない場合は、この属性を
None
に設定できます。
バージョン3.9で変更: Traceback.total_nframe 属性が追加されました。
- 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())