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
We can see that Python loaded 4855 KiB data (bytecode and constants) from
modules and that the collections module allocated 244 KiB to build
namedtuple types.
オプションの詳細については 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
We can see that Python has loaded 8173 KiB of module data (bytecode and
constants), and that this is 4428 KiB more than had been loaded before the
tests, when the previous snapshot was taken. 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. メモリブロックのトレースバックを取得する¶
最大のメモリブロックのトレースバックを表示するコードです:
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) と- -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()関数を参照してください。
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'に置換されます。- 例: - Filter(True, subprocess.__file__)は- subprocessモジュールのみを含みます
- Filter(False, tracemalloc.__file__)は- tracemallocモジュールのトレースを除外します
- Filter(False, "<unknown>")は空のトレースバックを除外します
 - バージョン 3.5 で変更: - '.pyo'ファイル拡張子が- '.py'に置換されることはもうありません。- 
inclusive¶
- If inclusive is - True(include), only trace memory blocks allocated in a file with a name matching- filename_patternat line number- lineno.- If inclusive is - False(exclude), ignore memory blocks allocated in a file with a name matching- filename_patternat line number- lineno.
 - 
lineno¶
- フィルタの行番号です ( - int)。lineno が- Noneの場合フィルタはあらゆる行番号にマッチします。
 - 
filename_pattern¶
- Filename pattern of the filter ( - 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, key_type: str, cumulative: bool=False)¶
- Compute the differences with an old snapshot. Get statistics as a sorted list of - StatisticDiffinstances grouped by key_type.- See the - Snapshot.statistics()method for key_type and cumulative parameters.- 結果は降順でソートされます: キーは - StatisticDiff.size_diffの絶対値、- StatisticDiff.size、- StatisticDiff.count_diffの絶対置、- Statistic.count、そして- StatisticDiff.tracebackです。
 - 
filter_traces(filters)¶
- Create a new - Snapshotinstance with a filtered- tracessequence, filters is a list of- Filterinstances. If filters is an empty list, return a new- Snapshotinstance 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. 
 - 
statistics(key_type: str, cumulative: bool=False)¶
- Get statistics as a sorted list of - Statisticinstances grouped by key_type:- key_type - description - 'filename'- ファイル名 - 'lineno'- ファイル名と行番号 - 'traceback'- traceback - If cumulative is - True, cumulate size and count of memory blocks of all frames of the traceback of a trace, not only the most recent frame. The cumulative mode can only be used with key_type equals to- 'filename'and- '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¶
- Sequence of - Frameinstances sorted from the most recent frame to the oldest frame.- A traceback contains at least - 1frame. If the- tracemallocmodule failed to get a frame, the filename- "<unknown>"at line number- 0is used.- When a snapshot is taken, tracebacks of traces are limited to - get_traceback_limit()frames. See the- take_snapshot()function.- The - Trace.tracebackattribute is an instance of- Tracebackinstance.- 
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 that- format()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()) 
 
- 
