メモリ管理—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.8/c-api/memory
移動先:案内検索

メモリ管理

概要

Pythonのメモリ管理には、すべてのPythonオブジェクトとデータ構造を含むプライベートヒープが含まれます。 このプライベートヒープの管理は、 Pythonメモリマネージャーによって内部的に保証されています。 Pythonメモリマネージャーには、共有、セグメンテーション、事前割り当て、キャッシュなど、さまざまな動的ストレージ管理の側面を処理するさまざまなコンポーネントがあります。

最下位レベルでは、rawメモリアロケータは、オペレーティングシステムのメモリマネージャと対話することにより、すべてのPython関連データを格納するための十分なスペースがプライベートヒープにあることを保証します。 生のメモリアロケータに加えて、複数のオブジェクト固有のアロケータが同じヒープ上で動作し、すべてのオブジェクトタイプの特性に適合した個別のメモリ管理ポリシーを実装します。 たとえば、整数は異なるストレージ要件と速度/スペースのトレードオフを意味するため、整数オブジェクトは、文字列、タプル、またはディクショナリとは異なる方法でヒープ内で管理されます。 したがって、Pythonメモリマネージャーは一部の作業をオブジェクト固有のアロケーターに委任しますが、後者がプライベートヒープの範囲内で動作することを保証します。

Pythonヒープの管理はインタプリタ自体によって実行され、そのヒープ内のメモリブロックへのオブジェクトポインタを定期的に操作する場合でも、ユーザーはそれを制御できないことを理解することが重要です。 Pythonオブジェクトおよびその他の内部バッファーへのヒープスペースの割り当ては、このドキュメントに記載されているPython / C API関数を介して、Pythonメモリマネージャーによってオンデマンドで実行されます。

メモリの破損を回避するために、拡張機能の作成者は、Cライブラリによってエクスポートされた関数malloc()calloc()realloc()free()を使用してPythonオブジェクトを操作しようとしないでください。 ]。 これにより、CアロケータとPythonメモリマネージャの間で呼び出しが混在し、致命的な結果が生じます。これらは異なるアルゴリズムを実装し、異なるヒープで動作するためです。 ただし、次の例に示すように、個々の目的のためにCライブラリアロケータを使用してメモリブロックを安全に割り当てたり解放したりできます。

PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
...Do some I/O operation involving buf...
res = PyBytes_FromString(buf);
free(buf); /* malloc'ed */
return res;

この例では、I / Oバッファのメモリ要求はCライブラリアロケータによって処理されます。 Pythonメモリマネージャは、結果として返されるバイトオブジェクトの割り当てにのみ関与します。

ただし、ほとんどの場合、Pythonヒープからメモリを割り当てることをお勧めします。これは、PythonヒープがPythonメモリマネージャーの制御下にあるためです。 たとえば、これは、インタプリタがCで記述された新しいオブジェクトタイプで拡張されている場合に必要です。 Pythonヒープを使用するもう1つの理由は、拡張モジュールのメモリニーズについてPythonメモリマネージャに通知したいという願望です。 要求されたメモリが内部の非常に特殊な目的にのみ使用される場合でも、すべてのメモリ要求をPythonメモリマネージャに委任すると、インタプリタはメモリフットプリント全体のより正確なイメージを得ることができます。 その結果、特定の状況下では、Pythonメモリマネージャーは、ガベージコレクション、メモリ圧縮、その他の予防手順などの適切なアクションをトリガーする場合とトリガーしない場合があります。 前の例で示したようにCライブラリアロケータを使用すると、I / Oバッファに割り当てられたメモリがPythonメモリマネージャから完全にエスケープされることに注意してください。

も参照してください

PYTHONMALLOC 環境変数を使用して、Pythonで使用されるメモリアロケータを構成できます。

PYTHONMALLOCSTATS 環境変数を使用して、新しいpymallocオブジェクトアリーナが作成されるたびに、およびシャットダウン時に pymallocメモリアロケータの統計を出力できます。


Rawメモリインターフェイス

次の関数セットは、システムアロケータのラッパーです。 これらの関数はスレッドセーフであり、 GIL を保持する必要はありません。

デフォルトのrawメモリアロケータは、次の関数を使用します:malloc()calloc()realloc()、およびfree(); ゼロバイトを要求するときは、malloc(1)(またはcalloc(1, 1))を呼び出します。

バージョン3.4の新機能。


void *PyMem_RawMalloc(size_t n)

n バイトを割り当て、割り当てられたメモリにタイプ void * のポインタを返します。要求が失敗した場合は、NULLを返します。

ゼロバイトを要求すると、PyMem_RawMalloc(1)が代わりに呼び出されたかのように、可能であれば、別個の非NULLポインタが返されます。 メモリはいかなる方法でも初期化されていません。

void *PyMem_RawCalloc(size_t nelem, size_t elsize)

バイト単位のサイズが elsize である nelem 要素を割り当て、割り当てられたメモリにタイプ void * のポインタを返します。割り当てられたメモリの場合は、NULLを返します。リクエストは失敗します。 メモリはゼロに初期化されます。

ゼロ要素またはサイズがゼロバイトの要素を要求すると、代わりにPyMem_RawCalloc(1, 1)が呼び出されたかのように、可能であれば、別個の非NULLポインターが返されます。

バージョン3.5の新機能。

void *PyMem_RawRealloc(void *p, size_t n)

p が指すメモリブロックのサイズを n バイトに変更します。 内容は、新旧の最小サイズに変更されません。

pNULLの場合、呼び出しはPyMem_RawMalloc(n)と同等です。 それ以外の場合、 n がゼロに等しい場合、メモリブロックのサイズは変更されますが解放されず、返されるポインタはNULLではありません。

pNULLでない限り、 PyMem_RawMalloc()PyMem_RawRealloc()、または PyMem_RawCallocへの前回の呼び出しによって返されている必要があります。 ()

要求が失敗した場合、 PyMem_RawRealloc()NULLを返し、 p は前のメモリ領域への有効なポインタのままです。

void PyMem_RawFree(void *p)

p が指すメモリブロックを解放します。これは、 PyMem_RawMalloc()PyMem_RawRealloc()、または PyMem_RawCalloc( )。 それ以外の場合、またはPyMem_RawFree(p)が以前に呼び出された場合、未定義の動作が発生します。

pNULLの場合、操作は実行されません。


メモリインターフェース

次の関数セットは、ANSI C標準をモデルにしていますが、ゼロバイトを要求するときの動作を指定しており、Pythonヒープからメモリを割り当てたり解放したりするために使用できます。

デフォルトのメモリアロケータpymallocメモリアロケータを使用します。

警告

これらの機能を使用する場合は、 GIL を保持する必要があります。


バージョン3.6で変更:デフォルトのアロケータは、システムmalloc()ではなくpymallocになりました。


void *PyMem_Malloc(size_t n)

n バイトを割り当て、割り当てられたメモリにタイプ void * のポインタを返します。要求が失敗した場合は、NULLを返します。

ゼロバイトを要求すると、PyMem_Malloc(1)が代わりに呼び出されたかのように、可能であれば、別個の非NULLポインタが返されます。 メモリはいかなる方法でも初期化されていません。

void *PyMem_Calloc(size_t nelem, size_t elsize)

バイト単位のサイズが elsize である nelem 要素を割り当て、割り当てられたメモリにタイプ void * のポインタを返します。割り当てられたメモリの場合は、NULLを返します。リクエストは失敗します。 メモリはゼロに初期化されます。

ゼロ要素またはサイズがゼロバイトの要素を要求すると、代わりにPyMem_Calloc(1, 1)が呼び出されたかのように、可能であれば、別個の非NULLポインターが返されます。

バージョン3.5の新機能。

void *PyMem_Realloc(void *p, size_t n)

p が指すメモリブロックのサイズを n バイトに変更します。 内容は、新旧の最小サイズに変更されません。

pNULLの場合、呼び出しはPyMem_Malloc(n)と同等です。 それ以外の場合、 n がゼロに等しい場合、メモリブロックのサイズは変更されますが解放されず、返されるポインタはNULLではありません。

pNULLでない限り、 PyMem_Malloc()PyMem_Realloc()、または PyMem_Callocへの前回の呼び出しによって返されている必要があります。 ()

要求が失敗した場合、 PyMem_Realloc()NULLを返し、 p は前のメモリ領域への有効なポインタのままです。

void PyMem_Free(void *p)

p が指すメモリブロックを解放します。これは、 PyMem_Malloc()PyMem_Realloc()、または PyMem_Calloc( )。 それ以外の場合、またはPyMem_Free(p)が以前に呼び出された場合、未定義の動作が発生します。

pNULLの場合、操作は実行されません。

以下のタイプ指向のマクロは、便宜上提供されています。 TYPE は任意のCタイプを指すことに注意してください。

TYPE *PyMem_New(TYPE, size_t n)
PyMem_Malloc()と同じですが、(n * sizeof(TYPE))バイトのメモリを割り当てます。 TYPE * にキャストされたポインタを返します。 メモリはいかなる方法でも初期化されていません。
TYPE *PyMem_Resize(void *p, TYPE, size_t n)

PyMem_Realloc()と同じですが、メモリブロックのサイズが(n * sizeof(TYPE))バイトに変更されます。 TYPE * にキャストされたポインタを返します。 戻ると、 p は新しいメモリ領域へのポインタになり、障害が発生した場合はNULLになります。

これはCプリプロセッサマクロです。 p は常に再割り当てされます。 エラーを処理するときにメモリが失われないように、 p の元の値を保存します。

void PyMem_Del(void *p)
PyMem_Free()と同じです。

さらに、上記のC API関数を使用せずに、Pythonメモリアロケータを直接呼び出すために、次のマクロセットが提供されています。 ただし、これらを使用してもPythonバージョン間のバイナリ互換性は維持されないため、拡張モジュールでは非推奨になることに注意してください。

  • PyMem_MALLOC(size)
  • PyMem_NEW(type, size)
  • PyMem_REALLOC(ptr, size)
  • PyMem_RESIZE(ptr, type, size)
  • PyMem_FREE(ptr)
  • PyMem_DEL(ptr)


オブジェクトアロケータ

次の関数セットは、ANSI C標準をモデルにしていますが、ゼロバイトを要求するときの動作を指定しており、Pythonヒープからメモリを割り当てたり解放したりするために使用できます。

デフォルトのオブジェクトアロケータpymallocメモリアロケータを使用します。

警告

これらの機能を使用する場合は、 GIL を保持する必要があります。


void *PyObject_Malloc(size_t n)

n バイトを割り当て、割り当てられたメモリにタイプ void * のポインタを返します。要求が失敗した場合は、NULLを返します。

ゼロバイトを要求すると、PyObject_Malloc(1)が代わりに呼び出されたかのように、可能であれば、別個の非NULLポインタが返されます。 メモリはいかなる方法でも初期化されていません。

void *PyObject_Calloc(size_t nelem, size_t elsize)

バイト単位のサイズが elsize である nelem 要素を割り当て、割り当てられたメモリにタイプ void * のポインタを返します。割り当てられたメモリの場合は、NULLを返します。リクエストは失敗します。 メモリはゼロに初期化されます。

ゼロ要素またはサイズがゼロバイトの要素を要求すると、代わりにPyObject_Calloc(1, 1)が呼び出されたかのように、可能であれば、別個の非NULLポインターが返されます。

バージョン3.5の新機能。

void *PyObject_Realloc(void *p, size_t n)

p が指すメモリブロックのサイズを n バイトに変更します。 内容は、新旧の最小サイズに変更されません。

pNULLの場合、呼び出しはPyObject_Malloc(n)と同等です。 それ以外の場合、 n がゼロに等しい場合、メモリブロックのサイズは変更されますが解放されず、返されるポインタはNULLではありません。

pNULLでない限り、 PyObject_Malloc()PyObject_Realloc()、または PyObject_Callocへの前回の呼び出しによって返されている必要があります。 ()

要求が失敗した場合、 PyObject_Realloc()NULLを返し、 p は前のメモリ領域への有効なポインタのままです。

void PyObject_Free(void *p)

p が指すメモリブロックを解放します。これは、 PyObject_Malloc()PyObject_Realloc()、または PyObject_Calloc( )。 それ以外の場合、またはPyObject_Free(p)が以前に呼び出された場合、未定義の動作が発生します。

pNULLの場合、操作は実行されません。


デフォルトのメモリアロケータ

デフォルトのメモリアロケータ:

構成 名前 PyMem_RawMalloc PyMem_Malloc PyObject_Malloc
リリースビルド "pymalloc" malloc pymalloc pymalloc
デバッグビルド "pymalloc_debug" malloc +デバッグ pymalloc +デバッグ pymalloc +デバッグ
pymallocなしでビルドをリリース "malloc" malloc malloc malloc
pymallocなしでビルドをデバッグします "malloc_debug" malloc +デバッグ malloc +デバッグ malloc +デバッグ

伝説:


メモリアロケータをカスタマイズする

バージョン3.4の新機能。


type PyMemAllocatorEx

メモリブロックアロケータを記述するために使用される構造。 構造には4つのフィールドがあります。

分野

意味

void *ctx

最初の引数として渡されたユーザーコンテキスト

void* malloc(void *ctx, size_t size)

メモリブロックを割り当てる

void* calloc(void *ctx, size_t nelem, size_t elsize)

ゼロで初期化されたメモリブロックを割り当てます

void* realloc(void *ctx, void *ptr, size_t new_size)

メモリブロックの割り当てまたはサイズ変更

void free(void *ctx, void *ptr)

メモリブロックを解放します

バージョン3.5での変更: PyMemAllocator構造の名前が PyMemAllocatorEx に変更され、新しいcallocフィールドが追加されました。

type PyMemAllocatorDomain

アロケータドメインを識別するために使用される列挙型。 ドメイン:

PYMEM_DOMAIN_RAW

関数:

PYMEM_DOMAIN_MEM

関数:

PYMEM_DOMAIN_OBJ

関数:

void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
指定されたドメインのメモリブロックアロケータを取得します。
void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)

指定したドメインのメモリブロックアロケータを設定します。

新しいアロケータは、ゼロバイトを要求するときに、NULL以外の個別のポインタを返す必要があります。

PYMEM_DOMAIN_RAWドメインの場合、アロケータはスレッドセーフである必要があります。 GIL は、アロケータが呼び出されたときに保持されません。

新しいアロケータがフックではない場合(前のアロケータを呼び出さない場合)、 PyMem_SetupDebugHooks()関数を呼び出して、新しいアロケータの上部にデバッグフックを再インストールする必要があります。

void PyMem_SetupDebugHooks(void)

Pythonメモリアロケータ関数のバグを検出するためのフックを設定します。

新しく割り当てられたメモリはバイト0xCDCLEANBYTE)で満たされ、解放されたメモリはバイト0xDDDEADBYTE)で満たされます。 メモリブロックは「禁止バイト」(FORBIDDENBYTE:バイト0xFD)で囲まれています。

ランタイムチェック:

  • API違反を検出します。例: PyMem_Malloc()によって割り当てられたバッファーで呼び出された PyObject_Free()

  • バッファの開始前に書き込みを検出します(バッファアンダーフロー)

  • バッファ終了後の書き込みを検出(バッファオーバーフロー)

  • PYMEM_DOMAIN_OBJ(例: PyObject_Malloc())およびPYMEM_DOMAIN_MEM(例: PyMem_Malloc( ))ドメインは呼び出されます

エラーの場合、デバッグフックは tracemalloc モジュールを使用して、メモリブロックが割り当てられたトレースバックを取得します。 トレースバックは、 tracemalloc がPythonのメモリ割り当てをトレースしていて、メモリブロックがトレースされている場合にのみ表示されます。

Pythonがデバッグモードでコンパイルされている場合、これらのフックはデフォルトでインストールされます。 PYTHONMALLOC 環境変数を使用して、リリースモードでコンパイルされたPythonにデバッグフックをインストールできます。

バージョン3.6で変更:この関数は、リリースモードでコンパイルされたPythonでも機能するようになりました。 エラー時に、デバッグフックは tracemalloc を使用して、メモリブロックが割り当てられたトレースバックを取得するようになりました。 デバッグフックは、PYMEM_DOMAIN_OBJおよびPYMEM_DOMAIN_MEMドメインの関数が呼び出されたときにGILが保持されているかどうかもチェックするようになりました。

バージョン3.8で変更:バイトパターン0xCBCLEANBYTE)、0xDBDEADBYTE)および0xFBFORBIDDENBYTE)は0xCD0xDD0xFDに置き換えられ、WindowsCRTデバッグmalloc()およびfree()


pymallocアロケータ

Pythonには、寿命の短い小さなオブジェクト(512バイト以下)用に最適化された pymalloc アロケータがあります。 256KiBの固定サイズの「アリーナ」と呼ばれるメモリマッピングを使用します。 512バイトを超える割り当ての場合は、 PyMem_RawMalloc()および PyMem_RawRealloc()にフォールバックします。

pymalloc は、PYMEM_DOMAIN_MEM(例: PyMem_Malloc())およびPYMEM_DOMAIN_OBJ(例:デフォルトアロケータです。 ] PyObject_Malloc())ドメイン。

アリーナアロケーターは次の機能を使用します。

  • WindowsのVirtualAlloc()およびVirtualFree()
  • mmap()およびmunmap()が利用可能な場合、
  • それ以外の場合は、malloc()およびfree()

pymalloc ArenaAllocatorをカスタマイズする

バージョン3.4の新機能。


type PyObjectArenaAllocator

アリーナアロケーターを説明するために使用される構造。 構造には3つのフィールドがあります。

分野

意味

void *ctx

最初の引数として渡されたユーザーコンテキスト

void* alloc(void *ctx, size_t size)

サイズバイトのアリーナを割り当てます

void free(void *ctx, void *ptr, size_t size)

アリーナを解放する

void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
アリーナアロケーターを入手してください。
void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
アリーナアロケーターを設定します。


tracemalloc C API

バージョン3.7の新機能。


int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)

tracemalloc モジュールで割り当てられたメモリブロックを追跡します。

成功した場合は0を返し、エラーの場合は-1を返します(トレースを格納するためのメモリの割り当てに失敗しました)。 tracemallocが無効になっている場合は、-2を返します。

メモリブロックがすでに追跡されている場合は、既存のトレースを更新します。

int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)

tracemalloc モジュールで割り当てられたメモリブロックの追跡を解除します。 ブロックが追跡されなかった場合は何もしません。

tracemallocが無効になっている場合は-2を返し、それ以外の場合は0を返します。


これはセクション概要の例で、最初の関数セットを使用してI / OバッファーがPythonヒープから割り当てられるように書き直されています。

PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* allocated with PyMem_Malloc */
return res;

タイプ指向の関数セットを使用した同じコード:

PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* for I/O */

if (buf == NULL)
    return PyErr_NoMemory();
/* ...Do some I/O operation involving buf... */
res = PyBytes_FromString(buf);
PyMem_Del(buf); /* allocated with PyMem_New */
return res;

上記の2つの例では、バッファーは常に同じセットに属する関数を介して操作されることに注意してください。 実際、特定のメモリブロックに同じメモリAPIファミリを使用する必要があるため、異なるアロケータが混在するリスクが最小限に抑えられます。 次のコードシーケンスには2つのエラーが含まれており、異なるヒープで動作する2つの異なるアロケータが混在しているため、そのうちの1つは fatal とラベル付けされています。

char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3);  /* Wrong -- should be PyMem_Free() */
free(buf2);       /* Right -- allocated via malloc() */
free(buf1);       /* Fatal -- should be PyMem_Del()  */

Pythonヒープからの生のメモリブロックの処理を目的とした関数に加えて、Pythonのオブジェクトは、 PyObject_New()PyObject_NewVar()、および PyObject_Del()で割り当てられ、解放されます。

これらについては、Cでの新しいオブジェクトタイプの定義と実装に関する次の章で説明します。