#9. 参照カウント

参照カウントは、CPython の主要なメモリ管理メカニズムです。すべての通常のオブジェクトには、現在そのオブジェクトを指している強参照の数が含まれています。このカウントがゼロになると、CPython はオブジェクトを即座に破棄できます。

この設計は、CPython と他の多くの言語ランタイムの最も明確な違いの 1 つです。 CPython には循環ガベージ コレクターがありますが、コレクターは参照カウントを補います。ほとんどのオブジェクトは、定期的なトレースではなく、参照カウントの遷移によって再利用されます。

9.1 核となるアイデア

Python オブジェクトは、何かがそのオブジェクトへの参照を所有している間は生き続けます。

概念的には:```text object created reference count = 1

another owner stores the object reference count += 1

an owner releases the object reference count -= 1

reference count reaches 0 object is deallocated 例:python x = [] y = x

del x del y ```リスト オブジェクトが作成され、バインドされます。x。バインディングy = x同じリストへの別の参照を作成します。削除中x参照を 1 つ削除します。削除中y残りの参照を削除するので、リストを破棄できます。

C レベルでは、これはオブジェクト ヘッダーによって制御されます。```c typedef struct { Py_ssize_t ob_refcnt; PyTypeObject *ob_type; } PyObject;


`ob_refcnt`は参照カウントフィールドです。

## 9.2 強参照

強参照はオブジェクトを存続させます。

通常の Python バインディングのほとんどは強参照です。```python
x = object()
items = [x]
d = {"key": x}
```ここで、オブジェクトには以下からの参照があります。```text
local name x
list element items[0]
dictionary value d["key"]
```少なくとも 1 つの強参照が残っている限り、オブジェクトを破棄することはできません。

コンテナはその要素への強い参照を保持します。リストには生の値は保存されません。 Python オブジェクトへのポインターを保存し、それらへの参照を所有します。```python
a = []
b = [a]
```リスト`b`リストへの参照を所有しています`a`

## 9.3 借用参照と所有参照

C API レベルでは、参照カウントは所有権ルールによって制御されます。

2 つの一般的なカテゴリがあります。

|参照種類 |意味 |
| ------------------ | ---------------------------------------------------------------------- |
|新しいリファレンス |呼び出し元は参照を所有しており、それを解放する必要があります。
|お借りした参考資料 |呼び出し元はオブジェクトを一時的に使用することはできますが、新しい参照を所有していません。

新しい参照の例:```c
PyObject *x = PyLong_FromLong(42);

/* x is a new reference */
Py_DECREF(x);

PyLong_FromLongオブジェクトを作成または返し、呼び出し元に 1 つの参照の所有権を与えます。

借用したリファレンスの例:```c PyObject *item = PyList_GetItem(list, 0);

/* item is borrowed */ ```電話をかける側は電話をかけてはならないPy_DECREF(item)最初に借用参照を所有参照に変換しない限り、Py_INCREF

正しいパターン:```c PyObject *item = PyList_GetItem(list, 0); if (item == NULL) { return NULL; }

Py_INCREF(item); /* now item is owned */

/* use item */

Py_DECREF(item);


##9.4`Py_INCREF`

`Py_INCREF`もう一人の所有者が現在強力な参照を保持していると記録されています。

概念的には:```c
Py_INCREF(obj);
```手段:```text
I am going to keep this object alive.
```典型的なケース:```text
store object into a container
store object into a struct field
return an existing object as a new reference
keep a borrowed reference beyond its safe lifetime
```例:```c
typedef struct {
    PyObject_HEAD
    PyObject *value;
} BoxObject;
```もし`BoxObject`別の Python オブジェクトを格納する場合は、参照カウントをインクリメントする必要があります。```c
static int
Box_set_value(BoxObject *self, PyObject *value)
{
    Py_INCREF(value);
    Py_XDECREF(self->value);
    self->value = value;
    return 0;
}
```ボックスは次の所有者になります`value`

## 9.5`Py_DECREF`

`Py_DECREF`参照の所有権を解放します。

概念的には:```c
Py_DECREF(obj);
```手段:```text
I no longer need to keep this object alive.
```参照カウントがゼロに達すると、CPython はオブジェクトを破棄します。

簡略化:```c
#define Py_DECREF(op)                  \
    do {                               \
        if (--(op)->ob_refcnt == 0) {  \
            dealloc(op);               \
        }                              \
    } while (0)
```実際の実装はさらに複雑ですが、これでルールが理解できます。

新しいリファレンスはすべて、最終的にはリリースされなければなりません。```c
PyObject *x = PyLong_FromLong(42);
if (x == NULL) {
    return NULL;
}

/* use x */

Py_DECREF(x);
```を忘れて`Py_DECREF`オブジェクトが漏れます。

電話をかける`Py_DECREF`回数が多すぎると、使用中にオブジェクトが破壊される可能性があります。

## 9.6`Py_XINCREF`そして`Py_XDECREF`いくつかのヒントは次のとおりです。`NULL`。`Py_INCREF`そして`Py_DECREF`有効なオブジェクト ポインターが必要です。彼らの`X`バリアントは受け入れます`NULL`。```c
Py_XINCREF(obj);
Py_XDECREF(obj);
```これらは通常、オプションのフィールドに使用されます。```c
typedef struct {
    PyObject_HEAD
    PyObject *name;   /* may be NULL */
} UserObject;
```デアロケータ:```c
static void
User_dealloc(UserObject *self)
{
    Py_XDECREF(self->name);
    Py_TYPE(self)->tp_free((PyObject *)self);
}
```もし`self->name``NULL``Py_XDECREF`何もしません。

## 9.7 Python レベルでの代入

Python の割り当てによりバインディングが変更されます。```python
x = []
x = {}
```最初の割り当てはバインドされます`x`リストに。 2 番目の割り当てはリバインドされます`x`辞書に。

CPython レベルでは、再バインドとは大まかに次のことを意味します。```text
increment reference to new object
store new pointer in local variable slot
decrement reference to previous object
```順序が重要です。 CPython は、特に代入に重複する参照やエラー パスが含まれる場合、オブジェクトを早期に破棄しないようにする必要があります。

例:```python
x = []
y = x
x = None
````x = None`、リストは生きたままです。`y`今でもそれを指します。

## 9.8 コンテナ自身の参照

オブジェクトがコンテナに挿入されると、コンテナは参照を所有します。```python
x = object()
items = []
items.append(x)
```オブジェクトは両方によって参照されています`x`そして`items[0]`

C レベルでは、コンテナはオブジェクトを格納するときに参照カウントをインクリメントする必要があります。

簡略化されたリスト追加ロジック:```text
append item to list
    ensure capacity
    increment item reference count
    store item pointer
    increase list size
```リストが破棄されると、その要素への参照が解放されます。```text
destroy list
    for each item:
        decrement item reference count
    free item array
    free list object
```この所有権モデルは再帰的です。他の参照が存在しない場合、コンテナを破棄すると、含まれているオブジェクトの破棄がトリガーされる可能性があります。

## 9.9 参照を安全に置換する

一般的な C パターンは、所有されているフィールドを別のフィールドに置き換えることです。

正しくない:```c
Py_DECREF(self->value);
self->value = new_value;
Py_INCREF(new_value);
```これは次の場合に失敗する可能性があります`new_value`と同じオブジェクトです`self->value`。の`Py_DECREF`インクリメントが発生する前にオブジェクトが破棄される可能性があります。

正しい:```c
Py_INCREF(new_value);
Py_DECREF(self->value);
self->value = new_value;
```Null 許容フィールドの場合:```c
Py_XINCREF(new_value);
Py_XDECREF(self->value);
self->value = new_value;
```このパターンは単純ですが重要です。```text
increment new reference first
decrement old reference second
store pointer when ownership is safe
```## 9.10 参照の盗用

一部の C API 関数は参照を盗みます。

参照を盗む関数は、呼び出し元から所有権を取得します。呼び出しが成功した後、呼び出し元はその参照をデクリメントしてはなりません。

パターン例:```c
PyObject *value = PyLong_FromLong(42);
if (value == NULL) {
    return NULL;
}

if (PyTuple_SetItem(tuple, 0, value) < 0) {
    Py_DECREF(value);
    return NULL;
}

/* do not Py_DECREF(value) here */

PyTuple_SetItemへの参照を盗みますvalue成功について。

この規則は、特に新しく作成されたオブジェクトからコンテナを構築する場合の効率性のために存在します。

参照の盗用は頻繁にバグの原因となります。ルールは、API ドキュメントまたは既知の C API 規約から読み取る必要があります。オブジェクト ポインターの型から推測することはできません。

9.11 エラーパス

参照カウントのバグはエラー パスで発生することがよくあります。

例:```c PyObject *a = PyLong_FromLong(1); if (a == NULL) { return NULL; }

PyObject b = PyLong_FromLong(2); if (b == NULL) { return NULL; / leak: a was not released */ } 正しい:c PyObject *a = PyLong_FromLong(1); if (a == NULL) { return NULL; }

PyObject *b = PyLong_FromLong(2); if (b == NULL) { Py_DECREF(a); return NULL; } 一般的なスタイルでは、1 つのクリーンアップ ブロックを使用します。c PyObject *a = NULL; PyObject *b = NULL; PyObject *result = NULL;

a = PyLong_FromLong(1); if (a == NULL) { goto error; }

b = PyLong_FromLong(2); if (b == NULL) { goto error; }

result = PyNumber_Add(a, b); if (result == NULL) { goto error; }

Py_DECREF(a); Py_DECREF(b); return result;

error: Py_XDECREF(a); Py_XDECREF(b); return NULL;


## 9.12 参照を返す

Python に公開される C 関数は通常、新しい参照を返します。

例:```c
static PyObject *
answer(PyObject *self, PyObject *args)
{
    return PyLong_FromLong(42);
}
```返されたオブジェクトは新しい参照です。通訳者はそれを受け取り、責任を負います。

既存のオブジェクトを返す場合は、最初にインクリメントします。```c
static PyObject *
get_cached_value(MyObject *self, PyObject *Py_UNUSED(ignored))
{
    Py_INCREF(self->cached_value);
    return self->cached_value;
}
```借用した参照をインクリメントせずに返すことは重大なバグです。```c
static PyObject *
bad_get_cached_value(MyObject *self, PyObject *Py_UNUSED(ignored))
{
    return self->cached_value;   /* wrong if this is borrowed */
}
```呼び出し元は戻り値の所有権を期待します。参照カウントがインクリメントされなかった場合、その後のデクリメントによって所有権がアンダーフローし、解放後の使用動作が発生する可能性があります。

##9.13`None`、`True`、 そして`False`戻る`None`が一般的です。

使用:```c
Py_RETURN_NONE;
```概念的には、これは増加します`None`そしてそれを返します。

同等の形状:```c
Py_INCREF(Py_None);
return Py_None;
```同様に:```c
Py_RETURN_TRUE;
Py_RETURN_FALSE;
```これらのマクロは間違いを回避し、意図を明確にします。

Python レベルでは:```python
def f():
    return None
```C レベルでも、関数は所有されている参照を返す必要があります。`None`物体。

## 9.14 一時的な参照

多くの操作では一時的な参照が作成されます。

例:```c
PyObject *a = PyLong_FromLong(10);
PyObject *b = PyLong_FromLong(20);
PyObject *sum = PyNumber_Add(a, b);
```ここ、`a``b` そして`sum`作成が成功すると、すべての参照が所有されます。

正しいクリーンアップ:```c
Py_DECREF(a);
Py_DECREF(b);

return sum;
```デクリメントしないでください`sum`呼び出し元が所有権を取得するため、それを返す前に。

この分割は重要です。```text
objects used temporarily inside the function
    DECREF before return

object returned to caller
    return as owned reference
```## 9.15 参照カウントと関数呼び出し

Python 関数が呼び出されるとき、引数はオブジェクト参照として渡されます。```python
def f(x):
    return x

obj = []
y = f(obj)
```オブジェクトはコピーされません。関数は同じリストへの参照を受け取ります。

インタプリタ レベルでは、関数呼び出し機構が引数オブジェクト、ローカル変数、スタック値、戻り値の参照を管理します。

概念的には:```text
caller has reference to obj
call passes reference into callee frame
callee local x refers to same object
return value refers to same object
callee frame is cleared
caller receives returned reference
```正確な仕組みは最適化されていますが、所有権は不変のままです。すべてのライブ参照を考慮する必要があります。

## 9.16 スタック参照

バイトコード インタープリターは、各フレーム内の値スタックを使用します。

バイトコード命令は、オブジェクト参照をプッシュおよびポップします。

例:```python
a + b
```概念的には:```text
LOAD_FAST a
    push reference to a

LOAD_FAST b
    push reference to b

BINARY_OP +
    pop two references
    compute result
    push result reference
```スタック エントリは参照です。値がポップされて不要になった場合、CPython は対応する参照を解放する必要があります。

したがって、評価ループは大規模な参照管理システムになります。通常の実行、例外、ジャンプ、リターン、およびフレームのティアダウンにわたって所有権を保持する必要があります。

## 9.17 参照カウントと例外

エラー処理には、慎重な参照のクリーンアップが必要です。

一時オブジェクトの作成後に操作が失敗したとします。```c
PyObject *name = PyUnicode_FromString("field");
if (name == NULL) {
    return NULL;
}

PyObject *value = PyObject_GetAttr(obj, name);
Py_DECREF(name);

if (value == NULL) {
    return NULL;
}

return value;
```もし`PyObject_GetAttr`失敗すると戻ってくる`NULL`そして例外を設定します。一時的な`name`戻る前にデクリメントする必要があります。

C 関数は次の戻り値を返して例外を報告します。`NULL`例外が設定されている間。

参照のクリーンアップと例外の伝播は別個の責任です。```text
release owned references
preserve or set exception state
return NULL
```## 9.18 参照リーク

参照リークは、強い参照が決して解放されない場合に発生します。

例:```c
static PyObject *
leaky(PyObject *self, PyObject *args)
{
    PyObject *x = PyLong_FromLong(42);
    if (x == NULL) {
        return NULL;
    }

    Py_RETURN_NONE;   /* leak: x was never DECREFed */
}
```正しい:```c
static PyObject *
fixed(PyObject *self, PyObject *args)
{
    PyObject *x = PyLong_FromLong(42);
    if (x == NULL) {
        return NULL;
    }

    Py_DECREF(x);
    Py_RETURN_NONE;
}
```参照リークは小規模である可能性がありますが、長時間実行されるプロセスでは深刻です。```text
web servers
workers
language servers
notebooks
daemon processes
embedded Python runtimes
```高温パスの漏れは際限なく拡大する可能性があります。

## 9.19 解放後の使用

use-after-free は、参照されたオブジェクトが破棄された後にコードがポインターを使用するときに発生します。

パターン例:```c
PyObject *item = PyList_GetItem(list, 0);  /* borrowed */

Py_DECREF(list);

/* item may now be invalid */
PyObject_Repr(item);
```もし`list`~への最後の強い参照を所有していました`item`、破壊する`list`も破壊されました`item`

正しい:```c
PyObject *item = PyList_GetItem(list, 0);
if (item == NULL) {
    return NULL;
}

Py_INCREF(item);

Py_DECREF(list);

/* item is still alive */
PyObject *repr = PyObject_Repr(item);

Py_DECREF(item);
return repr;
```借用された参照は、所有された参照に変換されない限り、所有者よりも存続してはならない。

## 9.20 サイクル

参照カウントだけではサイクルを再利用できません。

例:```python
a = []
b = []

a.append(b)
b.append(a)

del a
del b
```削除後`a`そして`b`2 つのリストは引き続き相互参照します。

概念的には:```text
list A ---> list B
list B ---> list A
```プログラムが参照カウントに到達できなくなっても、参照カウントはゼロにはなりません。

これが、CPython が循環ガベージ コレクターを備えている理由です。到達不可能なコンテナ オブジェクトのグループを見つけて、それらを安全に破壊します。

参照カウントは一般的なケースに対処します。循環 GC は、参照カウントでは認識できないケースを処理します。

## 9.21 ファイナライザーと破棄のタイミング

CPython は、最後の参照が消えるとすぐにオブジェクトを破棄することがよくあります。

例:```python
class Resource:
    def __del__(self):
        print("destroyed")

r = Resource()
del r
```CPythonでは、`__del__`多くの場合、直後に実行されます`del r`、他に参照が存在しないと仮定します。

ただし、移植可能な Python コードでは、正確な破棄タイミングに依存することは避けるべきです。他の実装では、異なるガベージ コレクション戦略が使用される場合があります。

確定的なリソース管理にはコンテキスト マネージャーを使用します。```python
with open("data.txt") as f:
    data = f.read()
```これは、ファイル オブジェクトの破棄に依存するよりも優れています。```python
f = open("data.txt")
data = f.read()
f = None
```参照カウントにより CPython プロンプトがクリーンアップされますが、`with`寿命をダイレクトに表現します。

## 9.22 参照カウントは監視可能ですが実装固有です

CPython は参照カウントを公開します。`sys.getrefcount````python
import sys

x = []
print(sys.getrefcount(x))
```報告される数値は通常、予想よりも高くなります。`x`の中へ`getrefcount`一時的な参照を作成します。

例:```python
import sys

x = []
print(sys.getrefcount(x))   # often 2, not 1
```参照の 1 つは地元の名前です。`x`。もう 1 つの一時参照は、関数呼び出しからのものです。

このツールは慎重に使用してください。これは CPython の動作の学習とデバッグに役立ちますが、実装の詳細が公開されます。

## 9.23 不滅のオブジェクト

最新の CPython は、プロセスの存続期間中存続するように意図された選択された値に不滅のオブジェクトを使用します。

不死オブジェクトは、単純な意味での通常の参照オブジェクトのように動作しません。その参照カウントには特別な値が使用される場合があり、通常のインクリメントまたはデクリメント操作により、その有効期間の変更を回避できる場合があります。

この最適化により、非常に一般的なオブジェクトのオーバーヘッドが削減され、一部の実行時不変条件が簡素化されます。

重要な例には、シングルトンのような値や頻繁に再利用される内部オブジェクトなど、ランタイムの基本となるオブジェクトが含まれます。

拡張機能作成者のルールは単純です。```text
always use Py_INCREF, Py_DECREF, Py_XINCREF, and Py_XDECREF
do not inspect or modify ob_refcnt directly
do not assume reference counts are ordinary small integers
```正しいコードは、通常のオブジェクトや不滅のオブジェクトでも動作します。

## 9.24 フリースレッド CPython と参照カウント

従来の CPython は、グローバル インタープリタ ロックを使用して多くの参照カウントの更新を保護します。フリースレッド ビルドでは、複数のスレッドが Python コードを同時に実行する可能性があるため、リファレンス管理には追加の機構が必要です。

これは内部実装の詳細に影響しますが、C API の所有権ルールは概念的な契約のままです。```text
own a new reference
    release it exactly once

borrow a reference
    do not release it

keep a borrowed reference longer
    increment it first
```内部では、ビルド モードとバージョンに応じて、バイアスされた参照カウント、遅延参照処理、アトミック操作、またはその他の手法が使用される場合があります。

拡張コードは、生の参照カウントのレイアウトや更新メカニズムに依存することを避けるべきです。

## 9.25 参照カウントの規律

C 拡張コードの実践的な規律:

|状況 |アクション |
| ------------------------------------- | -------------------------------------- |
|関数は新しい参照を返します |`Py_DECREF`終わったら |
|関数は借用した参照を返します |しないでください`Py_DECREF`|
|オブジェクトをフィールドに保存する |`Py_INCREF`保管する前に |
|フィールド内のオブジェクトを置き換える |`Py_INCREF`新しい、それでは`Py_DECREF`古い |
|既存のオブジェクトを返す |`Py_INCREF`戻る前に |
|参照取得後のエラー | | 返す前に所有する参照を解放します。
|ポインタは次のとおりです`NULL`|使用`Py_XDECREF`|
| API がリファレンスを盗む |転送が成功した後は解放しないでください。

ほとんどの参照バグは、これらのルールのいずれかに違反することで発生します。

## 9.26 最小限の正しいフィールド所有者

これは 1 つの Python オブジェクト フィールドを所有する小さなオブジェクトです。```c
typedef struct {
    PyObject_HEAD
    PyObject *value;
} BoxObject;
```初期化:```c
static int
Box_init(BoxObject *self, PyObject *args, PyObject *kwds)
{
    PyObject *value = NULL;

    if (!PyArg_ParseTuple(args, "O", &value)) {
        return -1;
    }

    Py_INCREF(value);
    self->value = value;

    return 0;
}
```割り当て解除:```c
static void
Box_dealloc(BoxObject *self)
{
    Py_XDECREF(self->value);
    Py_TYPE(self)->tp_free((PyObject *)self);
}
```ゲッター:```c
static PyObject *
Box_get_value(BoxObject *self, void *closure)
{
    Py_INCREF(self->value);
    return self->value;
}
```セッター:```c
static int
Box_set_value(BoxObject *self, PyObject *value, void *closure)
{
    if (value == NULL) {
        PyErr_SetString(PyExc_TypeError, "cannot delete value");
        return -1;
    }

    Py_INCREF(value);
    Py_XDECREF(self->value);
    self->value = value;

    return 0;
}
```これは基本的な所有権パターンを示しています。```text
own stored field
release stored field during destruction
return stored field as a new reference
replace field safely
```## 9.27 メンタルモデル

CPython C コードを読むときは、すべてのコードについて次の質問をしてください。`PyObject *`:

```text
Who owns this reference?
Was this returned as a new reference or borrowed reference?
Can this pointer be NULL?
What happens on the error path?
Does this container steal the reference?
Does this field need to INCREF when assigned?
Does this deallocator DECREF everything it owns?
Can a DECREF run arbitrary Python code through finalizers?
```最後の質問は微妙です。参照をデクリメントすると、オブジェクトが破壊される可能性があります。オブジェクトを破棄すると、ファイナライザーを呼び出すことができます。ファイナライザーは Python コードを実行できます。 Python コードはグローバル状態を変更する可能性があります。したがって、`Py_DECREF`大きな副作用が生じる可能性があります。

## 9.28 概要

参照カウントは、CPython の主要な有効期間メカニズムです。各通常のオブジェクトは、それを指す強参照の数を記録します。`Py_INCREF`所有権を取得します。`Py_DECREF`所有権を解放します。カウントがゼロに達すると、CPython はその型固有のデアロケーターを通じてオブジェクトを破棄します。

難しいのはカウンター自体ではありません。難しいのは所有権の規律です。 C 拡張コードは、新しい参照、借用された参照、盗まれた参照、Null 許容参照、一時参照、格納された参照、および返された参照を区別する必要があります。

参照カウントは、CPython の動作の多くを説明します。つまり、即時破棄、多くの場合の決定論的なクリーンアップ、拡張モジュールのルール、コンテナーの所有権、別個の循環ガベージ コレクターの必要性などです。