객체 생명주기¶
이 섹션에서는 객체의 생명주기 동안 특정 타입의 슬롯들이 서로 어떻게 연관되는지 설명합니다. 이 부분은 모든 슬롯에 대한 완전한 표준 참조를 제공하기 위한 목적이 아닙니다. 특정 슬롯에 대한 자세한 내용은 형 객체 구조 의 해당 슬롯별 문서를 참조하십시오.
생명 이벤트¶
아래 그림은 객체의 생명주기 동안 발생할 수 있는 이벤트의 순서를 보여줍니다. A*에서 *B*로 향하는 화살표는 이벤트 *A*가 발생한 후 이벤트 *B*가 발생할 수 있음을 나타내며, 화살표의 라벨은 *B*가 *A 이후에 발생하기 위해 충족되어야 하는 조건을 의미합니다.
설명:
해당 타입을 호출하여 새 객체를 생성할 때:
tp_init이 완료되면 객체를 사용할 준비가 됩니다.객체에 대한 마지막 참조가 제거된 후 어느 시점에:
객체가 finalized 로 표시되지 않은 경우, 이를 finalized 로 표시하고
tp_finalize함수를 호출함으로써 파이널라이즈될 수 있습니다. Python은 객체에 대한 마지막 참조가 삭제될 때 객체를 파이널라이즈하지 않습니다. 따라서tp_finalize가 항상 호출되도록 하려면PyObject_CallFinalizerFromDealloc()을 사용하십시오.객체가 finalized로 표시된 경우, 가비지 수집기가 객체가 보유한 참조를 정리하기 위해
tp_clear를 호출할 수 있습니다. 객체의 참조 횟수가 0이 될 때는 호출되지 않습니다.tp_dealloc가 객체를 파괴하기 위해 호출됩니다. 코드 중복을 피하기 위해,tp_dealloc은 일반적으로 객체의 참조를 해제하기 위해tp_clear를 호출합니다.tp_dealloc이 객체 파괴를 마치면, 메모리를 해제하기 위해tp_free(일반적으로 유형에 적절하게 자동으로 설정된PyObject_Free()또는PyObject_GC_Del())를 직접 호출합니다.
tp_finalize함수는 필요한 경우 객체에 대한 참조를 추가할 수 있습니다. 참조가 추가되면 객체가 resurrected (부활)되어 예정된 파괴가 중단됩니다. (객체를 부활시킬 수 있는 것은tp_finalize뿐이며,tp_clear및tp_dealloc은tp_finalize를 호출하지 않고는 이를 수행할 수 없습니다.) 객체를 부활시키는 것이 해당 객체의 finalized 표시를 제거하는지 여부는 상황에 따라 다릅니다. 현재 Python은 가비지 수거를 지원하는(즉,Py_TPFLAGS_HAVE_GC플래그가 설정된) 경우에는 부활한 객체에서 finalized 표식을 제거하지 않지만, 가비지 수거를 지원하지 않는 경우에는 해당 표식을 제거합니다. 이러한 동작 중 하나 또는 둘 다는 향후 변경될 수 있습니다.tp_dealloc은 객체 파괴를 돕기 위해 해당 코드를 재사용하고자 하는 경우,PyObject_CallFinalizerFromDealloc()을 통해 선택적으로tp_finalize를 호출할 수 있습니다. 이는 파괴 전tp_finalize가 항상 호출됨을 보장하기 때문에 권장됩니다. 예제 코드는tp_dealloc문서를 참조하십시오.If the object is a member of a cyclic isolate and either
tp_clearfails to break the reference cycle or the cyclic isolate is not detected (perhapsgc.disable()was called, or thePy_TPFLAGS_HAVE_GCflag was erroneously omitted in one of the involved types), the objects remain indefinitely uncollectable (they “leak”). Seegc.garbage.
객체가 가비지 수거를 지원하도록 표시된 경우(즉, tp_flags 에 Py_TPFLAGS_HAVE_GC 플래그가 설정된 경우), 다음과 같은 이벤트도 발생할 수 있습니다:
가비지 수집기는 때때로
tp_traverse를 호출하여 cyclic isolate 를 식별합니다.가비지 수집기가 cyclic isolate 를 발견하면, 그룹 내 객체 중 하나를 finalized 로 표시하고 해당 기능이 있는 경우
tp_finalize함수를 호출하여 파이널라이즈합니다. 이 과정은 cyclic isolate가 더 이상 존재하지 않거나 모든 객체가 파이널라이즈될 때까지 반복됩니다.tp_finalize는 cyclic isolate 외부에서 참조를 추가하여 객체를 부활시킬 수 있습니다. 새로운 참조로 인해 해당 그룹의 객체들은 더 이상 cyclic isolate를 형성하지 않게 됩니다(참조 순환은 여전히 존재할 수 있으나, 존재하는 경우에도 객체들이 더 이상 격리되지 않습니다).가비지 수집기가 cyclic isolate 를 발견하고 그룹 내의 모든 객체가 이미 finalized 로 표시된 경우, 가비지 수집기는 아직 정리되지 않은 하나 이상의 객체에 대해 각각의
tp_clear함수를 호출하여 정리합니다(동시에 수행될 수 있음). 이 과정은 cyclic isolate가 존재하고 모든 객체가 정리될 때까지 반복됩니다.
cyclic isolate 파괴¶
다음은 각 구성 객체가 파이널라이즈되거나 정리된 후에도 계속 존재하는 가상의 cyclic isolate 의 생명주기 단계를 나열한 것입니다. cyclic isolate가 이 모든 단계를 거친다면 메모리 누수가 발생한 것이며, 모든 객체가 정리되는 시점(혹은 그 이전)에 사라져야 합니다. cyclic isolate는 참조 순환이 끊어지거나 파이널라이저 부활로 인해 객체들이 더 이상 격리되지 않을 때 소멸됩니다(tp_finalize 참조).
Reach(도달 가능) (아직 cyclic isolate가 아님): 모든 객체가 정상적이고 도달 가능한 상태입니다. 참조 순환이 존재할 수 있지만, 외부 참조가 있다는 것은 객체들이 아직 격리되지 않았음을 의미합니다.
Unreachable but consistent: 외부에서 해당 순환 객체 그룹으로 들어오는 마지막 참조가 제거되어 객체들이 격리된 상태(즉, 기본적인 cyclic isolate 발생)입니다. 아직 그룹 내의 어떤 객체도 파이널라이즈되거나 정리되지 않았습니다. cyclic isolate는 향후 가비지 수집기가 실행될 때까지 이 단계에 머무릅니다(다음 실행 시 모든 객체를 스캔하지 않을 수도 있으므로 반드시 다음 실행인 것은 아닙니다).
파이널라이즈된 것과 되지 않은 것이 섞임: cyclic isolate 내의 객체들은 하나씩 파이널라이즈됩니다. 즉, 일정 기간 동안 cyclic isolate가 파이널라이즈된 객체와 그렇지 않은 객체가 섞인 상태로 구성됩니다. 파이널라이즈 순서는 정의되지 않으므로 임의로 보일 수 있습니다. 파이널라이즈된 객체는 파이널라이즈되지 않은 객체와 상호작용할 때 정상적으로 동작해야 하며, 파이널라이즈되지 않은 객체는 참조하는 대상 중 임의의 일부가 파이널라이즈되는 것을 견딜 수 있어야 합니다.
모두 파이널라이즈됨: cyclic isolate 내의 모든 객체가 청소되기 전에 모두 파이널라이즈됩니다.
파이널라이즈된 것과 처리된 것이 섞임: 객체들은 직렬 또는 병렬로(단, GIL 은 유지됨) 처리될 수 있으며, 어느 경우든 일부가 다른 것보다 먼저 완료됩니다. 파이널라이즈된 객체는 참조하는 대상 중 일부가 정리되는 것을 견딜 수 있어야 합니다. PEP 442 에서는 이 단계를 “cyclic trash”라고 부릅니다.
누수(Leaked): 그룹 내의 모든 객체가 파이널라이즈되고 처리된 후에도 cyclic isolate가 여전히 존재한다면, 해당 객체들은 영구적으로 수거되지 않은 상태로 남습니다(
gc.garbage참조). cyclic isolate가 이 단계에 도달했다는 것은 참여하는 객체의tp_clear메서드가 요구되는 대로 참조 순환을 끊는 데 실패했음을 의미하며, 이는 버그입니다.
tp_clear 이 존재하지 않는다면 Python은 참조 순환을 안전하게 끊을 방법이 없습니다. cyclic isolate 내의 객체를 단순히 파괴하면 댕글링 포인터가 발생하여, 파괴된 객체를 참조하는 객체가 자체적으로 파괴될 때 정의되지 않은 동작이 발생합니다. 정리 단계는 객체 파괴를 두 단계 과정으로 만듭니다. 먼저 tp_clear 가 호출되어 객체들을 서로 분리할 수 있을 정도로 부분적으로 파괴하고, 그 다음 tp_dealloc 이 호출되어 파괴를 완료합니다.
청소와 달리 파이널리제이션은 파괴의 단계가 아닙니다. 파이널라이즈된 객체는 설계상의 약속을 계속 이행함으로써 여전히 올바르게 동작해야 합니다. 객체의 파이널라이저는 임의의 Python 코드를 실행할 수 있으며, 참조를 추가하여 곧 닥칠 파괴를 막을 수도 있습니다. 파이널라이저가 파괴와 관련되는 유일한 점은 호출 순서입니다. 실행될 경우, 이는 tp_clear (호출 시)로 시작하고 tp_dealloc 으로 끝나는 파괴 과정 이전에 실행됩니다.
파이널리제이션 단계는 cyclic isolate 내의 객체를 안전하게 회수하는 데 필수적이지는 않지만, 그 존재로 인해 객체가 정리될 때 안정적으로 동작하도록 타입을 설계하기가 더 쉬워집니다. 객체를 정리하면 해당 객체가 손상되고 부분적으로 파괴된 상태로 남을 수 있으며, 이 경우 정리된 객체의 메서드를 호출하거나 속성에 접근하는 것이 안전하지 않을 수 있습니다. 파이널리제이션이 있으면 오직 파이널라이즈된 객체만이 정리된 객체와 상호작용할 가능성이 있으며, 파이널라이즈되지 않은 객체는 아직 처리되지 않은(하지만 파이널라이즈되었을 수도 있는) 객체하고만 상호작용하도록 보장됩니다.
가능한 상호작용을 요약하면 다음과 같습니다:
파이널라이즈되지 않은 객체는 파이널라이즈되지 않았거나 파이널라이즈된 객체를 참조하거나 그로부터 참조를 받을 수 있지만, 처리된(cleared) 객체와는 관계가 없습니다.
파이널라이즈된 객체는 파이널라이즈되지 않았거나 파이널라이즈되거나 처리된 모든 객체를 참조하거나 그로부터 참조를 받을 수 있습니다.
처리된(cleared) 객체는 파이널라이즈되었거나 처리된 객체를 참조하거나 그로부터 참조를 받을 수 있지만, 파이널라이즈되지 않은 객체와는 관계가 없습니다.
참조 순환이 없는 경우, 객체의 마지막 참조가 삭제되면 단순히 파괴할 수 있으며, 사용하지 않는 객체를 안전하게 회수하기 위해 파이널리제이션 및 청소 단계는 필요하지 않습니다. 하지만 모든 객체가 cyclic isolate에 참여했는지 여부와 상관없이 동일한 일련의 과정을 거치게 되면 타입 설계가 단순해지므로, 파괴 전에 tp_finalize 및 tp_clear 를 자동으로 호출하는 것이 유용할 수 있습니다. 현재 Python은 cyclic isolate를 파괴하기 위해 필요한 경우에만 tp_finalize 와 tp_clear 를 호출하며, 이는 향후 버전에서 변경될 수 있습니다.
함수:¶
메모리 할당 및 해제에 대해서는 힙에 객체 할당하기 를 참조하십시오.
-
void PyObject_CallFinalizer(PyObject *op)¶
Finalizes the object as described in
tp_finalize. Call this function (orPyObject_CallFinalizerFromDealloc()) instead of callingtp_finalizedirectly because this function may deduplicate multiple calls totp_finalize. Currently, calls are only deduplicated if the type supports garbage collection (i.e., thePy_TPFLAGS_HAVE_GCflag is set); this may change in the future.Added in version 3.4.
-
int PyObject_CallFinalizerFromDealloc(PyObject *op)¶
- …의 일부 안정 ABI 버전 3.15 이후로.
PyObject_CallFinalizer()와 동일하지만 객체의 파괴자(tp_dealloc) 시작 부분에서 호출되도록 설계되었습니다. 객체에 대한 어떠한 참조도 있어서는 안 됩니다. 만약 객체의 파이널라이저가 객체를 부활시키면 이 함수는 -1을 반환하며, 더 이상의 파괴 과정은 진행되지 않아야 합니다. 그렇지 않으면 이 함수는 0을 반환하고 파괴 절차가 정상적으로 계속됩니다.Added in version 3.4.
더 보기
예제 코드를 위한
tp_dealloc.