Python の「メモリ使用量」について初心者向けに解説

はじめに

Pythonでプログラムを開発していると、メモリ使用量について意識しなければならない場面があります。
大量のデータを扱ったり、連続的に処理を行うプログラムでは、意図せずメモリが膨大に消費されてしまうかもしれません。
特に大規模な処理を実装する場合、どのデータ構造を使い、どのようにプログラムを最適化するかが重要です。

一方で、初心者の皆さんはメモリの概念自体になじみが薄く、何をどうチェックすればいいかわからないこともあるでしょう。
そこでこの記事では、Pythonでのメモリ使用量に焦点をあて、コード例を交えながらわかりやすく解説します。
基本的な仕組みから、実務シーンでのメモリ管理のポイントまで整理して紹介します。

この記事を読むとわかること

  • Pythonでメモリがどのように使われているかの基本的な考え方
  • 代表的なデータ構造(リストやタプルなど)がメモリ使用量に与える影響
  • コードを通じたメモリ使用量の確認方法
  • 実務で役立つメモリ最適化の考え方や具体例

Pythonのメモリ使用量とは何か

Pythonのコードを実行するとき、処理内容に応じてメモリが確保されます。
変数やデータ構造、関数などを扱うたびに適切な領域が割り当てられ、その分だけメモリを使うというイメージです。

開発をするうえで、ふだんはメモリの詳細を常に意識しなくてもプログラムは動作します。
しかし、大きなデータを読み込む処理繰り返し演算を行うケースでは、メモリ使用量が大きくなることがあります。
そのため、必要に応じてメモリ使用量をチェックし、適切なデータ構造や手法を選ぶことが大切です。

メモリリークと効率的なメモリ管理

Pythonはガーベジコレクションと呼ばれる仕組みを用いて、不要になったオブジェクトを自動的に解放します。
ただし、参照が途切れていないオブジェクトや複雑な依存関係がある場合、思わぬメモリリークが起こることもあります。

次のようなケースに遭遇したときは、メモリ使用量に注目するとよいかもしれません。

  • 長時間動き続けるサービスが異常にメモリを消費する
  • ループ処理を繰り返すごとに使用メモリが増えていく
  • 画像やデータベースなど巨大な外部データを扱う

このような問題が疑われる場合、Pythonのメモリ管理を理解しておくと対処がしやすくなるでしょう。

メモリ使用量を可視化する方法

Pythonでメモリ使用量を可視化するには、いくつかの手段があります。
初心者でも取り組みやすい方法を、順番に紹介します。

sys.getsizeofを使った基本的な確認

メモリ使用量をざっくり調べる場合、標準ライブラリに含まれる sys モジュールが役立ちます。
その中にある getsizeof() 関数は、オブジェクトのサイズをバイト単位で返してくれます。

import sys

my_list = [1, 2, 3, 4, 5]
size_of_list = sys.getsizeof(my_list)
print("リストのサイズ(バイト):", size_of_list)

my_list というリストがどの程度のサイズを持っているかを確認できます。
注意点として、getsizeof() はオブジェクト本体の大きさを返すもので、要素が持つ内部データ分をすべて含むわけではありません。
より正確な測定をしたい場合は、ほかの手法と組み合わせることもあります。

memory_profilerを使った詳細な測定

もしもう少し細かくメモリの使用状況を追いたい場合は、追加のライブラリとして memory_profiler を検討してみてください。
インストールが必要ですが、コード内にデコレータを付与するだけでメモリを計測してくれます。

# もし利用する場合のイメージ例
from memory_profiler import profile

@profile
def create_large_list():
    data = []
    for i in range(1000000):
        data.append(i)
    return data

if __name__ == "__main__":
    create_large_list()

このコードを実行すると、関数ごとにメモリ使用量がどのように推移したかを表示してくれます。
何万件ものデータを扱うような重い処理をする場合には、とても便利です。

メモリ使用量の計測は処理を挟むタイミングで実施すると、どの部分が負荷の原因か把握しやすくなります。

データ構造によるメモリ使用量の違い

Pythonでは、リストやタプル、辞書などさまざまなデータ構造を使えます。
これらは内部的にメモリの使い方が異なるため、同じ要素数でもメモリ使用量が変わることがあります。

リストとタプル

リストは追加や削除が柔軟に行えるのに対し、タプルはイミュータブル(内容の変更不可)という特徴があります。
リストの方が汎用的ですが、要素の追加や削除のために余剰な領域を確保しているケースもあります。
一方、タプルは変更を想定しない構造であるため、リストに比べてメモリ消費が少しだけ抑えられる傾向があります。

辞書とセット

辞書(dict)とセット(set)はハッシュテーブルで要素を管理しています。
検索や要素追加が比較的高速ですが、その分だけハッシュテーブルの管理領域を持つため、メモリ使用量はリストやタプルに比べて大きくなりがちです。
大規模データを扱うときには、必要以上に辞書やセットを増やさない工夫も大切です。

コード例:リストとタプルのメモリサイズ比較

ここでは sys.getsizeof() を使って、同じ要素数のリストとタプルでどれくらいサイズが異なるか見てみましょう。

import sys

my_list = [i for i in range(1000)]
my_tuple = tuple(i for i in range(1000))

print("リストのサイズ:", sys.getsizeof(my_list), "バイト")
print("タプルのサイズ:", sys.getsizeof(my_tuple), "バイト")

このコードを実行すると、同じ要素数でもリストとタプルで値が異なることが確認できます。
実務でどちらを使うかは機能面の違いだけでなく、メモリ使用量も踏まえて判断するとよいかもしれません。

使い捨ての配列ならタプルで十分なときがあります。
可変である必要がなければ、メモリを節約できる可能性も考慮しましょう。

大規模データを扱う際の対処法

データ分析やウェブサービスなどで、大量のデータを扱うケースは珍しくありません。
このときにメモリ使用量を抑えるための工夫をいくつか紹介します。

ジェネレーターを活用する

リスト内包表記やタプル内包表記で大きなリストを一気に生成すると、メモリを一気に使うことになります。
そこで、必要なタイミングで要素を取り出すジェネレーターを活用する方法があります。

def huge_data_generator():
    for i in range(1000000):
        yield i

# リストに全部格納しないで、順次処理に回す
for item in huge_data_generator():
    # 大きなリストを抱えずに少しずつ処理
    pass

一度に大きな配列を作成せず、逐次的に処理を進められるため、メモリ負荷を軽減できる場合があります。

適切なデータ型を選ぶ

数値を扱う場合でも、実際は整数だけが必要なのか、浮動小数点が必要なのかで適切なデータ型を検討するとよいでしょう。
文字列なら、結合を多用するよりもリストに一時的に格納してから join() する方がメモリを有効活用できることがあります。

実務での具体的な活用シーン

実際の開発現場を想定しながら、メモリ使用量に気をつける具体的なケースを考えてみましょう。

1. Webアプリケーション

Webアプリケーションのサーバーサイドで、ユーザーからのリクエストを受け取り、大量のデータを処理するケースがあります。
一度にデータをメモリに読み込むと、サーバーリソースが圧迫されて応答が遅くなる可能性があります。

適切にストリーミング処理を導入することで、メモリに保持するデータを最小限に抑えることができます。
また、部分的にデータをキャッシュする方法も有効ですが、キャッシュのしすぎで逆にメモリを圧迫しないように注意が必要です。

2. データ分析・機械学習

機械学習やデータ分析では、何百万行ものデータを扱うことがよくあります。
NumPyやPandasなどのライブラリを使うと便利ですが、その分メモリを多く消費します。

使用する列を絞ったり、型を最適化したりすることでメモリ消費を減らすことができます。
さらに、学習データを分割してバッチ処理を行う方法も検討するとよいでしょう。

3. 定期的なメモリ監視

長期稼働するサービスや常駐アプリケーションでは、起動後にメモリがどのように推移しているのか定期的に監視することが大切です。
ログを取っておくことで、どのタイミングでメモリが急激に増えているかが把握できます。
それを手がかりにコードの改善ポイントを探しやすくなります。

稀に、外部ライブラリが意図せずメモリを多く使っていることもあります。
その場合は使用バージョンの変更や別のライブラリへの切り替えを検討する必要があります。

まとめ

今回はPython メモリ使用量について、初心者の皆さんに向けて基礎的な仕組みや具体的な対処方法を解説しました。
メモリの最適化は大規模処理や継続的なサービス運用を考えるうえで、とても重要なテーマです。

データ構造ごとの特徴や、メモリ計測ツールの使い方を知っておくと、想定外のメモリ問題に遭遇したときに冷静に対処できるでしょう。
また、日頃から小さなデータ構造の違いやジェネレーターの活用を意識しておくと、効率的なプログラムを書く習慣が身につきやすくなります。

少しずつ知識を積み重ねていけば、Pythonに限らずさまざまな言語やフレームワークでも応用できる考え方が身につくでしょう。
これを機に、ぜひメモリ使用量をチェックしながらコードを書いてみてください。

Pythonをマスターしよう

この記事で学んだPythonの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。