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

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

**環境** : 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/

```py3
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))
```

```py3:実行結果
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しても機能しません**。開発中の確認用に、同一モジュール内で実行してください。

```py3
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変数を拾っています。

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

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

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

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