メモリー管理

C/C++ などのプログラミング言語と違い、MicroPython は自動メモリー管理をサポートすることで、メモリー管理の詳細を開発者から隠蔽しています。自動メモリー管理とは、オペレーティングシステムやアプリケーションが、メモリの割り当てと解放を自動的に管理するために使う手法です。これにより、オブジェクトに割り当てられたメモリーの解放を忘れるといった問題がなくなります。また、自動メモリ管理は、すでに解放されているメモリーを使ってしまうという重大な問題を回避できます。自動メモリー管理には様々な形態がありますが、その1つがガベージコレクション(GC: Garbage Collection)です。

ガベージコレクションは通常2つの責任を負っています。

  1. 利用可能なメモリに新しいオブジェクトを割り当てる。
  2. 使わなくなったメモリーを解放する。

多くの GC アルゴリズムがありますが、MicroPython は「マーク&スイープ」(Mark and Sweep )というポリシーでメモリー管理を行います。このアルゴリズムは、ヒープを横断してすべての生きているオブジェクトをマークするマークフェーズと、ヒープを横断してすべてのマークされていないオブジェクトを回収するスイープフェーズを持っています。

MicroPython のガベージコレクション機能は gc 組込みモジュールで利用できます。

>>> x = 5
>>> x
5
>>> import gc
>>> gc.enable()
>>> gc.mem_alloc()
1312
>>> gc.mem_free()
2071392
>>> gc.collect()
19
>>> gc.disable()
>>>

gc.disable() を実行した場合でも、 gc.collect() でメモリー回収を起動できます。

オブジェクトモデル

すべての MicroPython オブジェクトは mp_obj_t データ型で参照されます。これは通常、ワードサイズ(つまり、ターゲットアーキテクチャ上のポインタと同じサイズ)で、32ビット(STM32、nRF、ESP32、Unix x86)または64ビット(Unix x64)であることが多いです。また、特定のオブジェクト表現では、ワードサイズより大きくなることもあります。たとえば OBJ_REPR_D は 32 ビットアーキテクチャ上であっても 64 ビットサイズの mp_obj_t を持ちます。

mp_obj_t は MicroPython オブジェクトを表します。たとえば、整数、浮動小数点数、型、辞書、クラスのインスタンスなどです。 真偽値や小さな整数のようなオブジェクトは、その値が直接 mp_obj_t の値に格納されるので、追加のメモリは必要ありません。他のオブジェクトはその値がメモリの他の場所(たとえば、ガベージコレクション対象のヒープ上)に格納され、その mp_obj_t はそのメモリへのポインターを持っています。 mp_obj_t 内の一部は、それがどのような種類のオブジェクトであるかを示すタグになっています。

利用可能な表現の具体的な詳細については py/mpconfig.h を参照してください。

ポインタータグ

ポインターはワードアラインされているので、 mp_obj_t に格納する場合、このオブジェクトハンドルの下位ビットはゼロになります。たとえば、32 ビットアーキテクチャでは、下位 2 ビットは 0 になります。

********|********|********|******00

これらのビットは、タグを格納するために予約されています。このタグに付加情報を格納することにより、オブジェクトに新しいフィールドを用意して情報を格納するような非効率化を避けています。MicroPython において、タグは小さな整数、隔離化した(サイズの小さな)文字列、具象オブジェクトを扱っているかどうかを示し、それぞれに異なるセマンティクスが適用されます。

小さい整数の場合、マッピングは次のようになります:

********|********|********|*******1

上の表現で、アスタリスクは実際の整数値を表しています。隔離化した文字列または即値オブジェクト(True など)の場合、 `` mp_obj_t`` 値のレイアウトはそれぞれ次のようになります:

********|********|********|*****010

********|********|********|*****110

上記のいずれでもない具象オブジェクトは、次のような形式をとります:

********|********|********|******00

上記のアスタリスクは、メモリー上の具象オブジェクトのアドレスに相当します。

オブジェクトの割当て

小さな整数の値は mp_obj_t に直接格納され、ヒープなどではなく、埋め込みで割り当てられることになります。そのため、小さな整数の作成はヒープに影響を与えません。同様に、すでにテキストデータが別の場所に保存されている隔離化文字列や、 None, False, True などの即値も同様です。

それ以外の具象オブジェクトはすべてヒープ上に確保されます。そのオブジェクト構造は、オブジェクトの型を格納するフィールドがオブジェクトヘッダに予約されているようなものです。

+++++++++++
+         +
+ 型      + オブジェクトヘッダー
+         +
+++++++++++
+         + オブジェクトアイテム
+         +
+         +
+++++++++++

ヒープの割当ての最小単位はブロックであり、その大きさは4マシンワード(32ビットマシンでは16バイト、64ビットマシンでは32バイト)です。また、ヒープに割り当てられた別の構造体が、各ブロック内のオブジェクトの割り当てを追跡します。この構造体はビットマップと呼ばれています。

../_images/bitmap.png

ビットマップは、ブロックが「空き」なのか「使用中」なのかを追跡し、ブロックごとに2ビットを使います。

マーク&スイープガベージコレクションは、ヒープ上に割り当てられたオブジェクトを管理し、また、まだ使用中のオブジェクトをマークするためにビットマップを利用します。これらの詳細の完全な実装は py/gc.c を参照してください。

割当て: ヒープレイアウト

ヒープはプール内のブロックから構成されるように配置されます。ブロックは異なるプロパティを持てます。

  • ATB(Allocation Table Byte): 設定されている場合、そのブロックは通常のブロックです。
  • FREE: フリーブロック
  • HEAD: ブロックのチェーンの先頭
  • TAIL: ブロックのチェーンの最後尾
  • MARK: マークされたヘッドブロック
  • FTB(Finaliser Table Byte): 設定されている場合,そのブロックはファイナライザを持ちます