# `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.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` モジュールがトレースするメモリブロックの最大時のサイズを現在のサイズに設定します。

  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 with `take_snapshot()` before a call to `reset_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` を返します。

  `start()` ならびに `stop()` 関数も参照してください。

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 space `domain`.

    If inclusive is `False` (exclude), match memory blocks not allocated in the address space `domain`.

  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` or `None`).

    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 matching `filename_pattern` at line number `lineno`.

    If inclusive is `False` (exclude), ignore memory blocks allocated in a file with a name matching `filename_pattern` at line number `lineno`.

  lineno

    フィルタの行番号です (`int`)。lineno が `None` の場合フィルタはあらゆる行番号にマッチします。

  filename_pattern

    フィルタのファイル名のパターンです (`str`)。読み出し専用のプロパティです。

  all_frames

    all_frames が `True` の場合トレースバックの全てのフレームをチェックします。all_frames が `False` の場合最新のフレームをチェックします。

    トレースバックの上限が `1` の場合この属性の影響はありません。 `get_traceback_limit()` 関数と `Snapshot.traceback_limit` 属性を参照してください。

### Frame

class tracemalloc.Frame

  トレースバックのフレームです。

  `Traceback` クラスは `Frame` インスタンスのシークエンスです。

  filename

    ファイル名 (`str`)。

  lineno

    行番号 (`int`)。

### 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` です。

  dump(filename)

    スナップショットをファイルに書き込みます。

    スナップショットをリロードするには `load()` を使用します。

  filter_traces(filters)

    Create a new `Snapshot` instance with a filtered `traces` sequence, filters is a list of `DomainFilter` and `Filter` instances. If filters is an empty list, return a new `Snapshot` 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.

  classmethod load(filename)

    スナップショットをファイルからロードします。

    `dump()` を参照してください。

  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`)。

  traceback

    メモリブロックが割り当てられているトレースバック。`Traceback` インスタンス。

### StatisticDiff

class tracemalloc.StatisticDiff

  新旧 `Snapshot` インスタンスのメモリ割り当ての統計差です。

  `Snapshot.compare_to()` は `StatisticDiff` インスタンスのリストを返します。 `Statistic` クラスも参照してください。

  count

    新しいスナップショット内のメモリブロックの数 (`int`) です。新しいスナップショット内でメモリブロックが解放された場合は `0` です。

  count_diff

    新旧スナップショットのメモリブロック数の差 (`int`) です。メモリブロックが新しいスナップショット内で割り当てられた場合は `0` です。

  size

    新しいスナップショット内のメモリブロックのバイト単位での総サイズ (`int`) です。新しいスナップショット内でメモリブロックが解放された場合は `0` です。

  size_diff

    新旧スナップショットのバイト単位での総サイズの差 (`int`) です。メモリブロックが新しいスナップショット内で割り当てられた場合は `0` です。

  traceback

    メモリブロックが割り当てられたトレースバックで、 `Traceback` のインスタンスです。

### 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

    メモリブロックが割り当てられているトレースバック。`Traceback` インスタンス。

### 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 the `tracemalloc` module failed to get a frame, the filename `"<unknown>"` at line number `0` is used.

  When a snapshot is taken, tracebacks of traces are limited to `get_traceback_limit()` frames. See the `take_snapshot()` function. The original number of frames of the traceback is stored in the `Traceback.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 the `abs(limit)` oldest frames. If most_recent_first is `True`, 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 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())