この記事のポイント
@profileデコレータを付けるだけで、どの行が何 MiB 消費しているかを行単位で表示できます- Ubuntu 24.04 では
pip install --break-system-packages memory-profiler(PEP 668 対応) - 実測:
allocate_big_list()のdata.append()行が 9.5 MiB を消費していることを特定 delしても Increment が 0.0 MiB なのは Python の GC が内部プールに保持するため(仕様どおり)mprof runを使うと、コードを変えずに実行全体のメモリ推移をタイムラインで記録できます
Pythonのプログラムが「どこでメモリを食っているのか」を特定したいとき、print(sys.getsizeof(x)) を並べても全体像は見えません。memory_profiler を使うと、関数の各行で何 MiB 増えたかをそのまま表示できます。
本記事では Ubuntu 24.04 LTS の Docker コンテナで実際にインストールして動かし、その実出力を載せています。バージョン:memory-profiler 0.61.0、Python 3.12.3(2026-06-22 実測)。
目次
- memory_profiler とは
- インストール手順(Ubuntu 24.04 / 22.04)
- @profile デコレータで行単位計測
- mprof run でタイムライン計測
- よくある落とし穴
- Ubuntu 22.04 vs 24.04 の差
- よくあるエラーと解決策
- まとめ
memory_profiler とは
memory_profiler は Python スクリプトのメモリ使用量を行単位でプロファイリングするサードパーティライブラリです。@profile デコレータを付けた関数を実行すると、各行の実行前後の RSS メモリ(MiB)と増分(Increment)が表形式で出力されます。
内部では psutil ライブラリを使ってプロセスのメモリを一定間隔でサンプリングしています。CPUプロファイラの cProfile と似た使い心地で、メモリ版として使えます。

2つの計測モードがあります。
- @profile デコレータ:特定の関数を行単位で計測。どの行が重いか即座に分かる
- mprof run:スクリプト全体を時系列で計測。コードを変えずに使えるのが利点
インストール手順(Ubuntu 24.04 / 22.04)
手順1:Python と pip を確認する
Ubuntu 24.04 には Python 3.12 が標準で入っています。まず環境を確認します。
Python 3.12.3
$ python3 -m pip –version
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
pip が入っていない場合は sudo apt install python3-pip で追加します。
手順2:memory_profiler をインストールする
Ubuntu 24.04 の pip に関する注意(PEP 668)
Ubuntu 24.04 では、システムの Python 環境を守るため、pip install に --break-system-packages フラグが必要です。Docker コンテナや仮想環境(venv)では問題なく動きますが、ホストの Ubuntu 直接では externally-managed-environment エラーが出ます。
$ pip install –break-system-packages memory-profiler psutil
Successfully installed memory-profiler-0.61.0 psutil-7.2.2
# Ubuntu 22.04 または venv 内
$ pip install memory-profiler psutil
Successfully installed memory-profiler-0.61.0 psutil-7.2.2

インストール後、バージョンを確認します。
Name: memory-profiler
Version: 0.61.0
Summary: A module for monitoring memory usage of a python program
Author: Fabian Pedregosa
Requires: psutil
@profile デコレータで行単位計測
計測したいコードを書く
メモリを計測したい関数に @profile を付けます。インポートは不要です(実行時に python3 -m memory_profiler 経由で自動注入されます)。
@profile
def allocate_big_list():
data = []
for i in range(100000):
data.append(str(i) * 10)
return data
@profile
def process_and_free():
big = [b”x” * 1024 for _ in range(5000)]
total = sum(len(b) for b in big)
del big # メモリを解放
return total
if __name__ == “__main__”:
result = allocate_big_list()
freed = process_and_free()
print(“processed”, freed, “bytes”)
実行する
python3 test_leak.py ではなく、python3 -m memory_profiler test_leak.py で実行します。
Filename: /tmp/test_leak.py
Line # Mem usage Increment Occurrences Line Contents
=============================================================
4 21.8 MiB 21.8 MiB 1 @profile
5 def allocate_big_list():
6 21.8 MiB 0.0 MiB 1 data = []
7 31.2 MiB 0.0 MiB 100001 for i in range(100000):
8 31.2 MiB 9.5 MiB 100000 data.append(str(i) * 10)
9 31.2 MiB 0.0 MiB 1 return data
11 31.2 MiB 31.2 MiB 1 @profile
12 def process_and_free():
13 31.2 MiB 0.0 MiB 5001 big = [b”x” * 1024 for _ in range(5000)]
14 31.2 MiB 0.0 MiB 10003 total = sum(len(b) for b in big)
15 31.2 MiB 0.0 MiB 1 del big
16 31.2 MiB 0.0 MiB 1 return total

出力の読み方
4つの列があります。
| 列名 | 意味 | 実測値の例 |
|---|---|---|
Mem usage |
その行の実行後のプロセス全体の RSS メモリ | 31.2 MiB |
Increment |
前の行からの増分(ここが大きいほど問題) | 9.5 MiB |
Occurrences |
その行が実行された回数(ループ回数) | 100000 |
Line Contents |
ソースコードの内容 | data.append(…) |
今回の実測では、行 8(data.append(str(i) * 10))が 9.5 MiB の Increment を示しています。10万回のループで文字列をリストに追加する処理がメモリの主な消費源だと分かります。

mprof run でタイムライン計測
@profile は特定の関数の内部を見るためのツールですが、「関数をまたいでどこからメモリが増え始めたか」を掴むには mprof コマンドが便利です。コードを一切変えずに使えます。
mprof run の実行
mprof: Sampling memory every 0.1s
running new process
running as a Python program…
done
$ ls mprofile_*.dat
mprofile_20260622043355.dat
実行後、mprofile_*.dat ファイルが生成されます。中身は「MEM <使用量MiB> <タイムスタンプ>」の単純なテキスト形式です。

今回の実測では、起動直後 0.5 MiB → grow_memory() 実行中に段階的に増加 → ピーク 29.0 MiB → del 後も 29.0 MiB を維持、という推移が記録されました。

グラフで可視化する(ローカル環境)
ローカルの Ubuntu(表示環境あり)では mprof plot でグラフ表示もできます。
$ pip install matplotlib
$ mprof plot
← 最新の .dat ファイルをグラフ表示
$ mprof plot –output memory.png
← PNG として保存(ヘッドレス環境でも使える)
サーバー(ヘッドレス)環境では –output でファイル保存
VPS や Docker コンテナなど表示環境がない場合、mprof plot だけだと「cannot connect to X server」エラーになります。--output memory.png オプションを付けて PNG に保存してから scp 等で手元に転送してください。
よくある落とし穴
①「del したのにメモリが減らない」は正常
実測で確認したように、del 後の Increment は 0.0 MiB でした。Python のメモリ管理では、delで参照を外しても GC がそのメモリを OS に返さず内部の「メモリプール」に保持します。次回の割り当てで再利用されるため、プロセスの RSS は下がらないことが多いです。
本当のリークかどうかは、プログラム実行全体を通じて Mem usage が一方向に増え続けるかどうかで判断します。
②import を忘れると NameError
python3 スクリプト名.py で直接実行した場合、@profile が未定義で NameError になります。必ず python3 -m memory_profiler スクリプト名.py で実行するか、スクリプト先頭に以下のフォールバックを入れます。
try:
from memory_profiler import profile
except ImportError:
def profile(func):
return func
③リスト内包表記の計測は不正確になることがある
big = [b"x" * 1024 for _ in range(5000)] のような1行の内包表記は、「実行中の途中」のメモリが行末時点でサンプリングされるため、実際の最大使用量より低く出ることがあります。詳細が必要な場合は for ループに展開して計測します。
Ubuntu 22.04 vs 24.04 の差
Ubuntu 22.04(Python 3.10.12)と Ubuntu 24.04(Python 3.12.3)で実際に計測した差をまとめます。

memory-profiler 0.61.0 / psutil 7.2.2 はどちらも同じバージョンです。Python 3.12.3 のほうがインタープリタ自体のベースラインが約 2 MiB 高い(21.8 MiB vs 19.5 MiB)ですが、これは Python 3.12 の内部メモリモデルの変更によるものです。実際のアプリケーションで計測する際は、実行環境に合わせて比較するようにします。
インストールコマンドは 22.04 と 24.04 で異なります。
| OS | Python | pip install コマンド | memory-profiler |
|---|---|---|---|
| Ubuntu 22.04 LTS | 3.10.12 | pip install memory-profiler psutil |
0.61.0 |
| Ubuntu 24.04 LTS | 3.12.3 | pip install --break-system-packages memory-profiler psutil |
0.61.0 |
よくあるエラーと解決策
error: externally-managed-environment
error: externally-managed-environment
× This environment is externally managed
…
Ubuntu 24.04 での PEP 668 エラーです。--break-system-packages フラグを付けるか、python3 -m venv ~/.venv && source ~/.venv/bin/activate で仮想環境を作ってからインストールします。
ModuleNotFoundError: No module named ‘memory_profiler’
インストール先と実行する Python が合っていない可能性があります。python3 -m pip show memory-profiler で Location を確認し、同じ Python で実行しているか確認します。
No module named ‘psutil’
memory_profiler は psutil に依存しています。pip install psutil を別途実行するか、pip install memory-profiler psutil でまとめてインストールします。
まとめ
memory_profiler を使うと、Python スクリプトのどの行が何 MiB 消費しているかを行単位で確認できます。今回の実測(Ubuntu 24.04 / Python 3.12.3)では、data.append(str(i) * 10) が 9.5 MiB の Increment を記録し、ループ内の文字列操作がメモリ消費の主因だと特定できました。
@profileデコレータ +python3 -m memory_profiler スクリプト.pyで行単位計測mprof run スクリプト.pyでタイムライン計測(コード変更不要)- Ubuntu 24.04 では
--break-system-packagesフラグが必要(venv を使うとフラグ不要) del後にメモリが減らないのは Python の GC 仕様で、リークではない- 本当のリークは Mem usage が一方向に増え続ける場合
VPS で Python サーバーを動かしているとメモリ不足になることがあります。その際は memory_profiler でボトルネックを特定してから最適化するのが効率的です。


コメント