Python

객체 생명주기

이 섹션에서는 객체의 생명주기 동안 특정 타입의 슬롯들이 서로 어떻게 연관되는지 설명합니다. 이 부분은 모든 슬롯에 대한 완전한 표준 참조를 제공하기 위한 목적이 아닙니다. 특정 슬롯에 대한 자세한 내용은 형 객체 구조 의 해당 슬롯별 문서를 참조하십시오.

생명 이벤트

아래 그림은 객체의 생명주기 동안 발생할 수 있는 이벤트의 순서를 보여줍니다. A*에서 *B*로 향하는 화살표는 이벤트 *A*가 발생한 후 이벤트 *B*가 발생할 수 있음을 나타내며, 화살표의 라벨은 *B*가 *A 이후에 발생하기 위해 충족되어야 하는 조건을 의미합니다.

Life Events tp_new tp_new start->tp_new    type call   tp_alloc tp_alloc tp_new->tp_alloc  direct call   tp_init tp_init tp_new->tp_init reachable reachable tp_init->reachable reachable->tp_init tp_traverse tp_traverse reachable->tp_traverse  not in a    cyclic    isolate   reachable->tp_traverse  periodic    cyclic isolate     detection   finalized? marked as finalized? reachable->finalized?  no refs   tp_finalize tp_finalize reachable->tp_finalize  resurrected    (maybe remove    finalized mark)   uncollectable uncollectable (leaked) reachable->uncollectable  cyclic    isolate    (no GC    support)   tp_dealloc tp_dealloc reachable->tp_dealloc  no refs tp_traverse->finalized?  cyclic    isolate   finalized?->tp_finalize  no (mark    as finalized)   tp_clear tp_clear finalized?->tp_clear  yes   tp_finalize->tp_clear  no refs or     cyclic isolate   tp_finalize->tp_dealloc  recommended  call (see  explanation) tp_finalize->tp_dealloc   no refs   tp_clear->uncollectable  cyclic    isolate   tp_clear->tp_dealloc  no refs   tp_free tp_free tp_dealloc->tp_free    direct call  

설명:

  • 해당 타입을 호출하여 새 객체를 생성할 때:

    1. 새 객체를 생성하기 위해 tp_new 가 호출됩니다.

    2. tp_new 는 새 객체의 메모리를 할당하기 위해 tp_alloc 을 직접 호출합니다.

    3. tp_init 이 새로 생성된 객체를 초기화합니다. 필요에 따라 객체를 재초기화하기 위해 tp_init 을 다시 호출할 수 있습니다. 또한 Python 코드가 __new__() 를 호출하는 경우와 같이 tp_init 호출이 완전히 생략될 수도 있습니다.

  • tp_init 이 완료되면 객체를 사용할 준비가 됩니다.

  • 객체에 대한 마지막 참조가 제거된 후 어느 시점에:

    1. 객체가 finalized 로 표시되지 않은 경우, 이를 finalized 로 표시하고 tp_finalize 함수를 호출함으로써 파이널라이즈될 수 있습니다. Python은 객체에 대한 마지막 참조가 삭제될 때 객체를 파이널라이즈하지 않습니다. 따라서 tp_finalize 가 항상 호출되도록 하려면 PyObject_CallFinalizerFromDealloc() 을 사용하십시오.

    2. 객체가 finalized로 표시된 경우, 가비지 수집기가 객체가 보유한 참조를 정리하기 위해 tp_clear 를 호출할 수 있습니다. 객체의 참조 횟수가 0이 될 때는 호출되지 않습니다.

    3. tp_dealloc 가 객체를 파괴하기 위해 호출됩니다. 코드 중복을 피하기 위해, tp_dealloc 은 일반적으로 객체의 참조를 해제하기 위해 tp_clear 를 호출합니다.

    4. tp_dealloc 이 객체 파괴를 마치면, 메모리를 해제하기 위해 tp_free (일반적으로 유형에 적절하게 자동으로 설정된 PyObject_Free() 또는 PyObject_GC_Del())를 직접 호출합니다.

  • tp_finalize 함수는 필요한 경우 객체에 대한 참조를 추가할 수 있습니다. 참조가 추가되면 객체가 resurrected (부활)되어 예정된 파괴가 중단됩니다. (객체를 부활시킬 수 있는 것은 tp_finalize 뿐이며, tp_cleartp_dealloctp_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_clear fails to break the reference cycle or the cyclic isolate is not detected (perhaps gc.disable() was called, or the Py_TPFLAGS_HAVE_GC flag was erroneously omitted in one of the involved types), the objects remain indefinitely uncollectable (they “leak”). See gc.garbage.

객체가 가비지 수거를 지원하도록 표시된 경우(즉, tp_flagsPy_TPFLAGS_HAVE_GC 플래그가 설정된 경우), 다음과 같은 이벤트도 발생할 수 있습니다:

  • 가비지 수집기는 때때로 tp_traverse 를 호출하여 cyclic isolate 를 식별합니다.

  • 가비지 수집기가 cyclic isolate 를 발견하면, 그룹 내 객체 중 하나를 finalized 로 표시하고 해당 기능이 있는 경우 tp_finalize 함수를 호출하여 파이널라이즈합니다. 이 과정은 cyclic isolate가 더 이상 존재하지 않거나 모든 객체가 파이널라이즈될 때까지 반복됩니다.

  • tp_finalizecyclic isolate 외부에서 참조를 추가하여 객체를 부활시킬 수 있습니다. 새로운 참조로 인해 해당 그룹의 객체들은 더 이상 cyclic isolate를 형성하지 않게 됩니다(참조 순환은 여전히 존재할 수 있으나, 존재하는 경우에도 객체들이 더 이상 격리되지 않습니다).

  • 가비지 수집기가 cyclic isolate 를 발견하고 그룹 내의 모든 객체가 이미 finalized 로 표시된 경우, 가비지 수집기는 아직 정리되지 않은 하나 이상의 객체에 대해 각각의 tp_clear 함수를 호출하여 정리합니다(동시에 수행될 수 있음). 이 과정은 cyclic isolate가 존재하고 모든 객체가 정리될 때까지 반복됩니다.

cyclic isolate 파괴

다음은 각 구성 객체가 파이널라이즈되거나 정리된 후에도 계속 존재하는 가상의 cyclic isolate 의 생명주기 단계를 나열한 것입니다. cyclic isolate가 이 모든 단계를 거친다면 메모리 누수가 발생한 것이며, 모든 객체가 정리되는 시점(혹은 그 이전)에 사라져야 합니다. cyclic isolate는 참조 순환이 끊어지거나 파이널라이저 부활로 인해 객체들이 더 이상 격리되지 않을 때 소멸됩니다(tp_finalize 참조).

  1. Reach(도달 가능) (아직 cyclic isolate가 아님): 모든 객체가 정상적이고 도달 가능한 상태입니다. 참조 순환이 존재할 수 있지만, 외부 참조가 있다는 것은 객체들이 아직 격리되지 않았음을 의미합니다.

  2. Unreachable but consistent: 외부에서 해당 순환 객체 그룹으로 들어오는 마지막 참조가 제거되어 객체들이 격리된 상태(즉, 기본적인 cyclic isolate 발생)입니다. 아직 그룹 내의 어떤 객체도 파이널라이즈되거나 정리되지 않았습니다. cyclic isolate는 향후 가비지 수집기가 실행될 때까지 이 단계에 머무릅니다(다음 실행 시 모든 객체를 스캔하지 않을 수도 있으므로 반드시 다음 실행인 것은 아닙니다).

  3. 파이널라이즈된 것과 되지 않은 것이 섞임: cyclic isolate 내의 객체들은 하나씩 파이널라이즈됩니다. 즉, 일정 기간 동안 cyclic isolate가 파이널라이즈된 객체와 그렇지 않은 객체가 섞인 상태로 구성됩니다. 파이널라이즈 순서는 정의되지 않으므로 임의로 보일 수 있습니다. 파이널라이즈된 객체는 파이널라이즈되지 않은 객체와 상호작용할 때 정상적으로 동작해야 하며, 파이널라이즈되지 않은 객체는 참조하는 대상 중 임의의 일부가 파이널라이즈되는 것을 견딜 수 있어야 합니다.

  4. 모두 파이널라이즈됨: cyclic isolate 내의 모든 객체가 청소되기 전에 모두 파이널라이즈됩니다.

  5. 파이널라이즈된 것과 처리된 것이 섞임: 객체들은 직렬 또는 병렬로(단, GIL 은 유지됨) 처리될 수 있으며, 어느 경우든 일부가 다른 것보다 먼저 완료됩니다. 파이널라이즈된 객체는 참조하는 대상 중 일부가 정리되는 것을 견딜 수 있어야 합니다. PEP 442 에서는 이 단계를 “cyclic trash”라고 부릅니다.

  6. 누수(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_finalizetp_clear 를 자동으로 호출하는 것이 유용할 수 있습니다. 현재 Python은 cyclic isolate를 파괴하기 위해 필요한 경우에만 tp_finalizetp_clear 를 호출하며, 이는 향후 버전에서 변경될 수 있습니다.

함수:

메모리 할당 및 해제에 대해서는 힙에 객체 할당하기 를 참조하십시오.

void PyObject_CallFinalizer(PyObject *op)

Finalizes the object as described in tp_finalize. Call this function (or PyObject_CallFinalizerFromDealloc()) instead of calling tp_finalize directly because this function may deduplicate multiple calls to tp_finalize. Currently, calls are only deduplicated if the type supports garbage collection (i.e., the Py_TPFLAGS_HAVE_GC flag 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.

분실물 보관소