Python

순환 가비지 수집 지원

순환 참조를 포함하는 가비지를 탐지하고 수집하는 파이썬의 지원은 역시 컨테이너일 수 있는 다른 객체의 “컨테이너” 인 객체 형의 지원이 필요합니다. 다른 객체에 대한 참조를 저장하지 않거나, 원자 형(가령 숫자나 문자열)에 대한 참조만 저장하는 형은 가비지 수집에 대한 어떤 명시적인 지원을 제공할 필요가 없습니다.

컨테이너형을 만들려면, 형 객체의 tp_flags 필드가 Py_TPFLAGS_HAVE_GC를 포함해야 하고 tp_traverse 처리기 구현을 제공해야 합니다. 형의 인스턴스가 가변이면, tp_clear 구현도 제공해야 합니다.

Py_TPFLAGS_HAVE_GC

이 플래그가 설정된 형의 객체는 여기에 설명된 규칙을 준수해야 합니다. 편의를 위해 이러한 객체를 컨테이너 객체라고 하겠습니다.

컨테이너형의 생성자는 두 가지 규칙을 준수해야 합니다:

  1. 객체의 메모리는 PyObject_GC_NewPyObject_GC_NewVar를 사용하여 할당해야 합니다.

  2. 다른 컨테이너에 대한 참조를 포함할 수 있는 모든 필드가 초기화되면, PyObject_GC_Track()를 호출해야 합니다.

마찬가지로, 객체의 할당해제자(deallocator)는 비슷한 규칙 쌍을 준수해야 합니다:

  1. 다른 컨테이너를 참조하는 필드가 무효화 되기 전에, PyObject_GC_UnTrack()를 호출해야 합니다.

  2. 객체의 메모리는 PyObject_GC_Del()를 사용하여 할당 해제되어야 합니다.

    경고

    형이 Py_TPFLAGS_HAVE_GC를 추가하는 경우, 적어도 tp_traverse 처리기를 구현하거나 그 하위 클래스에서 정의된 것을 명시적으로 사용해야 합니다.

    PyType_Ready() 또는 이를 간접적으로 호출하는 PyType_FromSpecWithBases(), PyType_FromSpec() 등의 API를 호출할 때, 해당 형이 가비지 컬렉터 프로토콜을 구현하는 클래스를 상속받고 자식 클래스가 Py_TPFLAGS_HAVE_GC 플래그를 포함하지 않는 경우 인터프리터가 tp_flags, tp_traverse, tp_clear 필드를 자동으로 채웁니다.

PyObject_GC_New(TYPE, typeobj)

PyObject_New와 유사하지만, Py_TPFLAGS_HAVE_GC 플래그가 설정된 컨테이너 객체를 위한 것.

객체를 위한 메모리를 할당하기 위해 이 함수를 직접 호출하지 마십시오. 대신 해당 형의 tp_alloc 슬롯을 호출하십시오.

형의 tp_alloc 슬롯을 채울 때, 단순히 이 매크로를 호출하는 사용자 정의 함수보다 PyType_GenericAlloc() 을 사용하는 것이 권장됩니다.

이 매크로에 의해 할당된 메모리는 PyObject_GC_Del() 을 사용하여 해제해야 합니다(보통 객체의 tp_free 슬롯을 통해 호출됩니다).

PyObject_GC_NewVar(TYPE, typeobj, size)

PyObject_NewVar와 유사하지만, Py_TPFLAGS_HAVE_GC 플래그가 설정된 컨테이너 객체를 위한 것.

객체를 위한 메모리를 할당하기 위해 이 함수를 직접 호출하지 마십시오. 대신 해당 형의 tp_alloc 슬롯을 호출하십시오.

형의 tp_alloc 슬롯을 채울 때, 단순히 이 매크로를 호출하는 사용자 정의 함수보다 PyType_GenericAlloc() 을 사용하는 것이 권장됩니다.

이 매크로에 의해 할당된 메모리는 PyObject_GC_Del() 을 사용하여 해제해야 합니다(보통 객체의 tp_free 슬롯을 통해 호출됩니다).

PyObject *PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
이것은 불안정 API. 마이너 릴리스에서 예고 없이 변경될 수 있습니다.

PyObject_GC_New 와 유사하지만 객체 끝에 extra_size 바이트를 할당합니다(오프셋 tp_basicsize). 할당된 메모리는 Python 객체 헤더 를 제외한 모든 영역이 0으로 초기화됩니다.

추가 데이터는 객체와 함께 해제되지만, 그 외의 경우에는 파이썬에 의해 관리되지 않습니다.

이 함수로 할당된 메모리는 PyObject_GC_Del() 을 사용하여 해제해야 합니다 (일반적으로 객체의 tp_free 슬롯을 통해 호출됩니다).

경고

인스턴스 뒤에 추가 데이터를 예약하는 최종 메커니즘이 아직 결정되지 않았으므로 이 함수는 불안정한(unstable) 것으로 표시됩니다. 가변적인 수의 필드를 할당하려면 대신 PyVarObjecttp_itemsize 를 사용하는 것이 좋습니다.

Added in version 3.12.

PyObject_GC_Resize(TYPE, op, newsize)

PyObject_NewVar에 의해 할당된 객체의 크기를 변경합니다. 형 TYPE* (모든 C 형을 나타냅니다) 의 크기가 조정된 객체나 실패하면 NULL을 반환합니다.

op는 형 PyVarObject*이어야 하고 아직 수거기가 추적하지 않아야 합니다. newsize는 형 Py_ssize_t이어야 합니다.

void PyObject_GC_Track(PyObject *op)
…의 일부 안정 ABI.

수거기가 추적하는 컨테이너 객체 집합에 객체 op를 추가합니다. 수거기는 예기치 않은 시간에 실행될 수 있으므로 추적되는 동안 객체가 유효해야 합니다. tp_traverse 처리기가 탐색하는 모든 필드가 유효해지면 호출해야 합니다, 보통 생성자의 끝부분 근처입니다.

int PyObject_IS_GC(PyObject *obj)

객체가 가비지 수거기 프로토콜을 구현하면 0이 아닌 값을 반환하고, 그렇지 않으면 0을 반환합니다.

이 함수가 0을 반환하면 가비지 수거기가 객체를 추적할 수 없습니다.

int PyObject_GC_IsTracked(PyObject *op)
…의 일부 안정 ABI 버전 3.9 이후로.

op의 객체 형이 GC 프로토콜을 구현하고 op가 현재 가비지 수거기가 추적 중이면 1을 반환하고 그렇지 않으면 0을 반환합니다.

이것은 파이썬 함수 gc.is_tracked()에 해당합니다.

Added in version 3.9.

int PyObject_GC_IsFinalized(PyObject *op)
…의 일부 안정 ABI 버전 3.9 이후로.

op의 객체 형이 GC 프로토콜을 구현하고 가비지 수거기가 op를 이미 파이널라이즈 했으면 1을 반환하고 그렇지 않으면 0을 반환합니다.

이것은 파이썬 함수 gc.is_finalized()에 해당합니다.

Added in version 3.9.

void PyObject_GC_Del(void *op)
…의 일부 안정 ABI.

PyObject_GC_NewPyObject_GC_NewVar를 사용하여 객체에 할당된 메모리를 해제합니다.

객체의 메모리를 해제하기 위해 이 함수를 직접 호출하지 마십시오. 대신 해당 형의 tp_free 슬롯을 호출하십시오.

PyObject_New, PyObject_NewVar 또는 관련 할당 함수로 할당된 메모리에 이를 사용하지 마십시오. 대신 PyObject_Free() 를 사용하십시오.

더 보기

void PyObject_GC_UnTrack(void *op)
…의 일부 안정 ABI.

수거기가 추적하는 컨테이너 객체 집합에서 op 객체를 제거합니다. PyObject_GC_Track()를 이 객체에 대해 다시 호출하여 추적 객체 집합에 다시 추가할 수 있음에 유의하십시오. 할당해제자(tp_dealloc 처리기)는 tp_traverse 처리기에서 사용하는 필드가 무효화 되기 전에 객체에 대해 이 함수를 호출해야 합니다.

버전 3.8에서 변경: _PyObject_GC_TRACK()_PyObject_GC_UNTRACK() 매크로는 공용 C API에서 제거되었습니다.

tp_traverse 처리기는 다음과 같은 형의 함수 매개 변수를 받아들입니다:

typedef int (*visitproc)(PyObject *object, void *arg)
…의 일부 안정 ABI.

tp_traverse 처리기에 전달되는 방문자 함수의 형. 이 함수는 탐색하는 객체를 object로, tp_traverse 처리기의 세 번째 매개 변수를 arg로 호출되어야 합니다. 파이썬 코어는 순환 가비지 탐지를 구현하기 위해 여러 방문자 함수를 사용합니다; 사용자가 자신의 방문자 함수를 작성해야 할 필요는 없습니다.

tp_clear 처리기는 inquiry 형이거나 객체가 불변이면 NULL이어야 합니다.

typedef int (*inquiry)(PyObject *self)
…의 일부 안정 ABI.

참조 순환을 생성했을 수 있는 참조를 삭제합니다. 불변 객체는 참조 순환을 직접 생성할 수 없으므로, 이 메서드를 정의 할 필요가 없습니다. 이 메서드를 호출한 후에도 객체가 유효해야 합니다 (단지 참조에 대해 Py_DECREF()를 호출하지 마십시오). 이 객체가 참조 순환에 참여하고 있음을 수거기가 감지하면 이 메서드를 호출합니다.

순회

tp_traverse 처리기는 다음 형이어야 합니다:

typedef int (*traverseproc)(PyObject *self, visitproc visit, void *arg)
…의 일부 안정 ABI.

가비지 컬렉션이 적용되는 객체의 순회 함수로, 가비지 컬렉터가 참조 사이클을 감지하는 데 사용됩니다. 구현 시 self 에 직접 포함된 각 객체에 대해 visit 함수를 호출해야 하며, 이때 visit 의 매개변수는 해당 객체와 처리기로 전달된 arg 값이 됩니다. visit 함수는 NULL 객체 인자와 함께 호출되어서는 안 됩니다. visit 이 0이 아닌 값을 반환하는 경우 그 값을 즉시 반환해야 합니다.

전형적인 tp_traverse 함수는 인스턴스가 소유한 파이썬 객체인 각 멤버에 대해 Py_VISIT() 편의 매크로를 호출합니다. 예를 들어, 다음은 threading.local 클래스의 (약간 오래된) 순회 함수입니다:

static int
local_traverse(PyObject *op, visitproc visit, void *arg)
{
    localobject *self = (localobject *) op;
    Py_VISIT(Py_TYPE(self));
    Py_VISIT(self->args);
    Py_VISIT(self->kw);
    Py_VISIT(self->dict);
    return 0;
}

참고

Py_VISIT()local_traverse()visitarg 매개변수가 이 특정 이름을 가져야 할 것을 요구합니다. 임의의 이름으로 명명하지 마십시오.

힙 할당 형 의 인스턴스는 자신의 형에 대한 참조를 보유합니다. 따라서 이들의 순회 함수는 해당 형을 방문해야 합니다:

Py_VISIT(Py_TYPE(self));

대신, 형은 힙 할당된 슈퍼클래스(또는 해당되는 경우 다른 힙 할당형)의 tp_traverse 를 호출함으로써 이 책임을 위임할 수 있습니다. 그렇게 하지 않으면 해당 형 객체가 가비지 컬렉션될 수 없습니다.

tp_flags 필드에 Py_TPFLAGS_MANAGED_DICT 비트가 설정된 경우, 순회 함수는 다음과 같이 PyObject_VisitManagedDict() 를 호출해야 합니다:

int err = PyObject_VisitManagedDict((PyObject*)self, visit, arg);
if (err) {
    return err;
}

인스턴스가 소유하는(즉, 이에 대한 강한 참조 를 갖는) 멤버만 방문해야 합니다. 예를 들어, 객체가 tp_weaklist 슬롯을 통해 약한 참조를 지원하는 경우, 연결 리스트를 지원하는 포인터( tp_weaklist 가 가리키는 대상)는 인스턴스가 자신에 대한 약한 참조를 직접 소유하지 않으므로 방문해서는 안 됩니다.

순회 함수에는 다음과 같은 제한이 있습니다:

경고

순회 함수는 어떠한 부수 효과도 가져서는 안 됩니다. 구현체는 직접 또는 간접적으로 어떤 파이썬 객체의 참조 횟수를 수정해서도 안 되며, 파이썬 객체를 생성하거나 파괴해서도 안 됩니다.

이는 대다수의 파이썬 C API 함수가 사용될 수 없음을 의미합니다. 해당 함수들은 새로운 예외를 발생시키거나, 결과 객체에 대한 새 참조를 반환하거나, 부수 효과를 사용하는 내부 로직을 포함할 수 있기 때문입니다. 또한 명시적으로 다른 내용이 기술되지 않는 한, 우연히 부수 효과가 없는 것으로 보이는 기능들도 경고 없이 향후 버전에서 부수 효과를 가질 수 있습니다.

안전한 함수 목록은 아래의 별도 섹션 을 참조하십시오.

참고

Py_VISIT() 호출은 참조 순환에 참여할 수 없음이 증명된 멤버의 경우 생략될 수 있습니다. 위의 local_traverse 예제에도 self->key 멤버가 있지만, 이 멤버는 NULL 또는 파이썬 문자열만 가능하므로 참조 순환의 일부가 될 수 없습니다.

반대로, 특정 멤버가 순환의 일부가 될 수 없다는 것을 알고 있더라도 디버깅을 돕기 위해 해당 멤버를 방문하도록 설정하여 gc 모듈의 get_referents() 함수에 포함되도록 할 수도 있습니다.

참고

tp_traverse 함수는 모든 스레드에서 호출될 수 있습니다.

가비지 수거는 “stop-the-world” 작업입니다. free threading 빌드에서도 tp_traverse 핸들러가 실행될 때는 단 하나의 스레드 상태만이 attached 상태가 됩니다.

버전 3.9에서 변경: 힙에 할당된 타입은 tp_traverse 에서 Py_TYPE(self) 를 방문해야 합니다. 이전 버전의 파이썬에서는 bug 40217 때문에 이 동작이 하위 클래스에서 충돌을 일으킬 수 있었습니다.

tp_traverse 핸들러 작성을 단순화하기 위해 Py_VISIT() 매크로가 제공됩니다. 이 매크로를 사용하려면 tp_traverse 구현이 인자 이름을 정확히 visitarg 으로 명명해야 합니다.

Py_VISIT(o)

PyObject* oNULL 이 아니면 인자 oarg 를 사용하여 visit 콜백을 호출합니다. visit 이 0이 아닌 값을 반환하면 해당 값을 반환합니다.

이는 대략 다음과 대응됩니다:

#define Py_VISIT(o)                             \
   if (op) {                                    \
      int visit_result = visit(o, arg);         \
      if (visit_result != 0) {                  \
         return visit_result;                   \
      }                                         \
   }

순회 안전 함수

다음 함수와 매크로는 tp_traverse 핸들러에서 안전하게 사용할 수 있습니다.

“DuringGC” 함수

다음 함수들은 오직 tp_traverse 핸들러에서만 사용해야 합니다. 다른 컨텍스트에서 호출할 경우 의도하지 않은 결과가 발생할 수 있습니다.

이 함수들은 _DuringGC 접미사가 없는 대응 함수들과 동일하게 동작하지만, 부작용이 없음을 보장하며 실패 시 예외를 발생시키지 않고 개별 문서에 명시된 대로 borrowed references 를 반환하거나 설정합니다.

이 함수들이 실패하여 NULL 또는 -1``을 반환할 있지만, 예외를 설정하지 않기 때문에 오류 정보를 확인할 없습니다. 어떤 경우에는 실패가 성공적인 ``NULL 결과와 구분되지 않을 수 있습니다.

void *PyObject_GetTypeData_DuringGC(PyObject *o, PyTypeObject *cls)
void *PyObject_GetItemData_DuringGC(PyObject *o)
void *PyType_GetModuleState_DuringGC(PyTypeObject *type)
void *PyModule_GetState_DuringGC(PyObject *module)
int PyModule_GetToken_DuringGC(PyObject *module, void **result)
…의 일부 안정 ABI 버전 3.15 이후로.

공통 정보는 “DuringGC” 함수 를 참조하십시오.

Added in version 3.15.

int PyType_GetBaseByToken_DuringGC(PyTypeObject *type, void *tp_token, PyTypeObject **result)
…의 일부 안정 ABI 버전 3.15 이후로.

공통 정보는 “DuringGC” 함수 를 참조하십시오.

*result 을 강한 참조가 아닌 borrowed reference 로 설정합니다. 이 참조는 tp_traverse 핸들러 호출이 지속되는 동안 유효합니다.

Added in version 3.15.

PyObject *PyType_GetModule_DuringGC(PyTypeObject *type)
PyObject *PyType_GetModuleByToken_DuringGC(PyTypeObject *type, const void *mod_token)
반환값: 빌린 참조. …의 일부 안정 ABI 버전 3.15 이후로.

공통 정보는 “DuringGC” 함수 를 참조하십시오.

이 함수들은 tp_traverse 핸들러 호출이 지속되는 동안 유효한 borrowed reference 를 반환합니다.

Added in version 3.15.

가비지 수거기 상태 제어하기

C-API는 가비지 수거 실행을 제어하기 위해 다음 함수들을 제공합니다.

Py_ssize_t PyGC_Collect(void)
…의 일부 안정 ABI.

가비지 수거기가 활성화되어 있는 경우 전체 가비지 수거를 수행합니다. (gc.collect() 은 조건에 상관없이 실행됨을 유의하십시오.)

수거되었으나 도달할 수 없어 수거되지 못한 객체의 수를 반환합니다. 가비지 수거기가 비활성화되어 있거나 이미 수거 중인 경우 즉시 0 을 반환합니다. 가비지 수거 중 발생하는 오류는 sys.unraisablehook 으로 전달됩니다. 이 함수는 예외를 발생시키지 않습니다.

int PyGC_Enable(void)
…의 일부 안정 ABI 버전 3.10 이후로.

가비지 수거기를 활성화합니다. gc.enable() 과 유사하며, 이전 상태를 반환합니다(비활성화 시 0, 활성화 시 1).

Added in version 3.10.

int PyGC_Disable(void)
…의 일부 안정 ABI 버전 3.10 이후로.

가비지 수거기를 비활성화합니다. gc.disable() 과 유사하며, 이전 상태를 반환합니다(비활성화 시 0, 활성화 시 1).

Added in version 3.10.

int PyGC_IsEnabled(void)
…의 일부 안정 ABI 버전 3.10 이후로.

가비지 수거기 상태를 조회합니다. gc.isenabled() 와 유사하며, 현재 상태를 반환합니다(비활성화 시 0, 활성화 시 1).

Added in version 3.10.

가비지 수거기 상태 조회하기

C-API는 가비지 수거기에 대한 정보를 조회하기 위해 다음 인터페이스를 제공합니다.

void PyUnstable_GC_VisitObjects(gcvisitobjects_t callback, void *arg)
이것은 불안정 API. 마이너 릴리스에서 예고 없이 변경될 수 있습니다.

모든 활성 GC 가능 객체에 대해 제공된 callback*을 실행합니다. *arg*은 모든 *callback 호출 시 그대로 전달됩니다.

경고

콜백에 의해 새로운 객체가 할당되거나 해제되는 경우, 해당 객체들이 방문될지 여부는 정의되지 않습니다.

동작 중에 가비지 수거가 비활성화됩니다. 콜백 내에서 명시적으로 수거를 실행하면 동일한 객체를 여러 번 방문하거나 전혀 방문하지 않는 등 정의되지 않은 동작이 발생할 수 있습니다.

Added in version 3.12.

typedef int (*gcvisitobjects_t)(PyObject *object, void *arg)

Type of the visitor function to be passed to PyUnstable_GC_VisitObjects(). arg is the same as the arg passed to PyUnstable_GC_VisitObjects. Return 1 to continue iteration, return 0 to stop iteration. Other return values are reserved for now so behavior on returning anything else is undefined.

Added in version 3.12.

분실물 보관소