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.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.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" 以上でなければなりません。

   "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. 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 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.


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 で変更: Added the "domain" attribute.

   domain

      Address space of a memory block ("int" or "None").

   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

      Filename pattern of the filter ("str"). Read-only property.

   all_frames

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

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


27.7.2.4. Frame
---------------

class tracemalloc.Frame

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

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

   filename

      ファイル名 ("str")。

   lineno

      行番号 ("int")。


27.7.2.5. 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 "StatisticDiff" instances 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" です
      。

   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)

      Get statistics as a sorted list of "Statistic" instances 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.6. Statistic
-------------------

class tracemalloc.Statistic

   メモリ割り当ての統計です。

   "Snapshot.statistics()" は "Statistic" インスタンスの一覧を返します
   。

   "StatisticDiff" クラスも参照してください。

   count

      メモリブロック数 ("int")。

   size

      メモリブロックのバイト単位の総サイズ ("int")。

   traceback

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


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" です。

   traceback

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


27.7.2.8. Trace
---------------

class tracemalloc.Trace

   メモリブロックをトレースします。

   "Snapshot.traces" 属性は "Trace" インスタンスのシークエンスです。

   size

      メモリブロックのバイト単位のサイズ ("int")。

   traceback

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


27.7.2.9. Traceback
-------------------

class tracemalloc.Traceback

   Sequence of "Frame" instances sorted from the most recent frame to
   the oldest 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 "Trace.traceback" attribute is an instance of "Traceback"
   instance.

   format(limit=None)

      Format the traceback as a list of lines with newlines.  Use the
      "linecache" module 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())
