0Pricing
Python Academy · レッスン

tracemalloc によるメモリープロファイリング

tracemalloc でメモリー割り当てを追跡し、リークを見つけます。

「tracemalloc によるメモリープロファイリング」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。

tracemallocとは

tracemallocは、ファイル名と行番号ごとにPythonのメモリ割り当てを追跡する標準ライブラリモジュールです。

import tracemalloc

tracemalloc.start()

x = [i for i in range(100_000)]

current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()

print(f"Current: {current/1024:.1f} KB")
print(f"Peak:    {peak/1024:.1f} KB")

スナップショットの取得

スナップショットは、ある時点での現在のすべてのメモリ割り当てを記録します。2つのスナップショットを比較すると、その間に割り当てられたメモリを特定できます。

import tracemalloc

tracemalloc.start()

snap1 = tracemalloc.take_snapshot()

data = [{"key": str(i)} for i in range(10_000)]

snap2 = tracemalloc.take_snapshot()

stats = snap2.compare_to(snap1, "lineno")
for stat in stats[:5]:
    print(stat)

スナップショットのフィルタリング

統計情報をアプリケーションのコードだけに絞り込み、標準ライブラリを除外して表示します。

import tracemalloc

tracemalloc.start()
leak_function()
snap = tracemalloc.take_snapshot()

filters = [tracemalloc.Filter(True, "*/myapp/*")]
stats = snap.filter_traces(filters).statistics("lineno")
for stat in stats[:10]:
    print(stat)

メモリを多く消費する箇所

snapshot.statistics("lineno")または"traceback"を使用すると、行ごとまたは完全なトレースバックごとに、メモリを多く消費している箇所を確認できます。

import tracemalloc

tracemalloc.start(nframe=10)   # track 10 frames deep

big_list = [i**2 for i in range(500_000)]

snap = tracemalloc.take_snapshot()
for stat in snap.statistics("traceback")[:3]:
    print(stat)
    for line in stat.traceback.format():
        print("  ", line)

メモリリークの特定

メモリリークが疑われる箇所の前後で2つのスナップショットを取得して比較します。スナップショット間で統計値が増加していれば、メモリリークの可能性があります。

import tracemalloc

tracemalloc.start()
snap_before = tracemalloc.take_snapshot()

for _ in range(100):
    suspected_leaky_function()

snap_after = tracemalloc.take_snapshot()
diff = snap_after.compare_to(snap_before, "lineno")
for stat in diff[:5]:
    print(stat)

get_traced_memory()

tracemalloc.get_traced_memory()は(current, peak)というバイト数の組を返します。軽量な実行前後の確認に便利です。

import tracemalloc

tracemalloc.start()
before, _ = tracemalloc.get_traced_memory()

do_something()

after, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()

print(f"Delta: {(after-before)/1024:.1f} KB, Peak: {peak/1024:.1f} KB")

memory_profiler

memory_profilerは、CPUに対するline_profilerと同様に、行ごとのメモリ使用量を提供します。

# pip install memory_profiler
from memory_profiler import profile

@profile
def build_data():
    data = [i**2 for i in range(100_000)]
    filtered = [x for x in data if x % 2 == 0]
    return filtered

build_data()
# python -m memory_profiler script.py

memray

memray(Bloomberg製)は、フレームグラフを出力できる高速なPythonメモリプロファイラです。

# pip install memray
# python -m memray run script.py
# python -m memray flamegraph memray-script.py.bin

# Generates an interactive HTML flamegraph
# showing allocations by call stack

メモリ使用量の削減

一般的な手法には、リストの代わりにジェネレータを使う、__slots__を使う、数値データにarrayモジュールを使う、numpy配列を使う、クロージャで大きなオブジェクトを保持し続けない、といったものがあります。

# Instead of:
data = [process(x) for x in huge_list]   # all in memory

# Use a generator:
def gen_data(lst):
    for x in lst:
        yield process(x)

for item in gen_data(huge_list):
    consume(item)   # one at a time

sys.getsizeof()

sys.getsizeof(obj)は、オブジェクトが使用するメモリ量をバイト単位で返します(浅いサイズであり、参照先のオブジェクトは含みません)。

import sys

print(sys.getsizeof([]))          # 56
print(sys.getsizeof([1,2,3]))     # 88
print(sys.getsizeof("hello"))     # 54
print(sys.getsizeof({}))          # 64

# For deep size, use recursive measurement:
def deep_size(obj):
    return sys.getsizeof(obj) + sum(
        sys.getsizeof(v) for v in vars(obj).values()
        if hasattr(obj, "__dict__")
    )

本番環境でのプロファイリング

本番環境ではnframesを小さく制限してtracemallocを実行し、オーバーヘッドを最小限に抑えます。後でオフライン分析できるよう、スナップショットをファイルに保存します。

import tracemalloc, pickle

tracemalloc.start(nframe=5)   # low overhead

# ... serve some requests ...

snap = tracemalloc.take_snapshot()
with open("snap.pkl","wb") as f:
    pickle.dump(snap, f)
# Analyse later offline

確認問題

tracemalloc.get_traced_memory()が返す2つの値は何を表しますか。

まとめ

tracemalloc.start()とスナップショットを使ってメモリリークを特定します。compare_to()でスナップショットを比較します。行ごとの使用量の確認にはmemory_profilerを、フレームグラフの作成にはmemrayを使います。ジェネレータ、__slots__、NumPy配列を使ってメモリ使用量を削減します。

よくある質問

「tracemalloc によるメモリープロファイリング」レッスンは無料ですか?

はい。「tracemalloc によるメモリープロファイリング」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。

「tracemalloc によるメモリープロファイリング」で何を学びますか?

tracemalloc でメモリー割り当てを追跡し、リークを見つけます。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Python Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「tracemalloc によるメモリープロファイリング」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPython Academyレッスンでコードを書いて実行できますか?

はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. CPython の参照カウント
  2. ガベージコレクターと循環参照
  3. cProfile と line_profiler によるプロファイリング
  4. tracemalloc によるメモリープロファイリング
← Python Academyに戻る