LoginSignup
29
23

More than 5 years have passed since last update.

[Python] 一定のメモリサイズを超えるオブジェクトをリストアップする

Last updated at Posted at 2017-11-04

メモリを消費しているオブジェクトをチェック!

無駄にメモリを消費しているオブジェクトがないか、チェックしてみましょう。

環境 : Python3

特定のオブジェクトのサイズ(メモリ使用量)

※2017/11/5 : コメントのご指摘で気づいたので追加
オブジェクトのサイズは、 sys.getsizeof() で取得できますが、listなどのコンテナ型オブジェクトは、実際に格納しているオブジェクトのサイズを取得できません。
そこで、Python公式ドキュメントで紹介されているように、以下のようなめんどくさい方法を取る必要があります。
[Python公式]
https://docs.python.jp/3/library/sys.html#sys.getsizeof
[リンク先: recursive sizeof recipe]
https://code.activestate.com/recipes/577504/

import sys
from itertools import chain
from collections import deque

def compute_object_size(o, handlers={}):
    dict_handler = lambda d: chain.from_iterable(d.items())
    all_handlers = {tuple: iter,
                    list: iter,
                    deque: iter,
                    dict: dict_handler,
                    set: iter,
                    frozenset: iter,
                   }
    all_handlers.update(handlers)     # user handlers take precedence
    seen = set()                      # track which object id's have already been seen
    default_size = sys.getsizeof(0)       # estimate sizeof object without __sizeof__

    def sizeof(o):
        if id(o) in seen:       # do not double count the same object
            return 0
        seen.add(id(o))
        s = sys.getsizeof(o, default_size)

        for typ, handler in all_handlers.items():
            if isinstance(o, typ):
                s += sum(map(sizeof, handler(o)))
                break
        return s

    return sizeof(o)

# 実行
d1 = np.random.randint(0, 1000, (1000, 1000))
d2 = np.random.randn(1000, 1000)
d_list = [d1, d2]
print('sys.getsizeof(d1)                    :', sys.getsizeof(d1))
print('compute_object_size(d1, unit=0)      :', compute_object_size(d1, unit=0))
print('sys.getsizeof(d_list)                :', sys.getsizeof(d_list))
print('compute_object_size(d_list, unit=0)  :', compute_object_size(d_list, unit=0))
実行結果
sys.getsizeof(d1)                    : 8000112
compute_object_size(d1, unit=0)      : 8000112.0
sys.getsizeof(d_list)                : 80
compute_object_size(d_list, unit=0)  : 16000304.0

一定のメモリサイズを超えるオブジェクト一覧

上の関数を使って、一定のメモリサイズを超えるオブジェクト一覧を表示します。
eval() は、文字列を変数に変換する関数です。
globals()を対象にしている為、この関数をモジュール化してimportしても機能しません。開発中の確認用に、同一モジュール内で実行してください。

import numpy as np
import pandas as pd

def show_objects_size(threshold, unit=2):
    """
    生きている全部の変数のサイズを表示する

    Parameters
    ----------
    threshold : int, float
        表示するサイズの下限しきい値。
        unitに応じた値にすること。
    unit : int
        表示するサイズの単位
        1: KB
        2: MB
        3: GB
    ex.
        100MB超の変数をGB単位で表示したい場合
        threshold=0.1, unit=3

    Returns
    -------
        なし
    """
    disp_unit = {0: 'bites', 1: 'KB', 2: 'MB', 3: 'GB'}
    # 処理中に変数が変動しないように固定
    globals_copy = globals().copy()
    for object_name in globals_copy.keys():
        size = compute_object_size(eval(object_name))
        if size > threshold:
            print('{:<15}{:.3f} {}'.format(object_name, size, disp_unit[unit]))

# 100MB超のオブジェクト一覧を表示する
show_object_size(100)

※Jupyter Notebookで記述するような、フラットな構成を想定して、global変数を拾っています。

実行結果(例)
train          2060.736 MB
test           749.182 MB
df_customer    443.340 MB
df_sales       238.667 MB

無駄なオブジェクトは削除

役目を終えた大きなサイズのオブジェクトは、メモリを圧迫しないように削除しておきましょう。

del train, test
# どれほど有効かわからないけど、適宜gc.collect()でガベージコレクションを強制実行。
import gc
gc.collect()

さいごに

Pandas.DataFrameについては、なぜメモリが増大してしまうのか、どう対処すればよいか等をこちらの記事に書きました。ご参考までに。
Pandas.DataFrameのメモリサイズを削減する(最大で8分の1) [Python]

29
23
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
23