Ciclo de vida do objeto¶
Esta seção explica como os slots de um tipo se relacionam entre si ao longo da vida de um objeto. Não se destina a ser uma referência canônica completa para os slots; em vez disso, consulte a documentação específica do slot em Type Object Structures para obter detalhes sobre um slot específico.
Eventos de vida¶
A figura abaixo ilustra a ordem dos eventos que podem ocorrer ao longo da vida de um objeto. Uma seta de A para B indica que o evento B pode ocorrer após a ocorrência do evento A, com o rótulo da seta indicando a condição que deve ser verdadeira para que B ocorra após A.
Explicação:
Quando um novo objeto é construído chamando seu tipo:
tp_new
é chamado para criar um novo objeto.tp_alloc
é chamado diretamente portp_new
para alocar a memória para o novo objeto.tp_init
inicializa o objeto recém-criado.tp_init
pode ser chamado novamente para reinicializar um objeto, se desejado. A chamada DEtp_init
também pode ser completamente ignorada, por exemplo, com código Python chamando__new__()
.
Após a conclusão de
tp_init
, o objeto estará pronto para uso.Algum tempo após a última referência a um objeto ser removida:
Se um objeto não estiver marcado como finalizado, ele poderá ser finalizado marcando-o como finalizado e chamando sua função
tp_finalize
. O Python não finaliza um objeto quando a última referência a ele é excluída; usePyObject_CallFinalizerFromDealloc()
para garantir quetp_finalize
seja sempre chamado.Se o objeto estiver marcado como finalizado,
tp_clear
poderá ser chamado pelo coletor de lixo para limpar as referências mantidas pelo objeto. Ele não é chamado quando a contagem de referências do objeto chega a zero.tp_dealloc
é chamado para destruir o objeto. Para evitar duplicação de código,tp_dealloc
normalmente chamatp_clear
para liberar as referências do objeto.Quando
tp_dealloc
termina a destruição do objeto, ele chama diretamentetp_free
(geralmente definido comoPyObject_Free()
ouPyObject_GC_Del()
automaticamente, conforme apropriado para o tipo) para desalocar a memória.
A função
tp_finalize
tem permissão para adicionar uma referência ao objeto, se desejado. Se isso acontecer, o objeto será ressuscitado, impedindo sua destruição pendente. (Somentetp_finalize
tem permissão para ressuscitar um objeto;tp_clear
etp_dealloc
não podem sem chamartp_finalize
.) Ressuscitar um objeto pode ou não causar a remoção da marca finalizado do objeto. Atualmente, o Python não remove a marca finalizado de um objeto ressuscitado se ele suportar coleta de lixo (ou seja, o sinalizadorPy_TPFLAGS_HAVE_GC
estiver definido), mas remove a marca se o objeto não suportar coleta de lixo; qualquer um ou ambos os comportamentos podem mudar no futuro.tp_dealloc
pode opcionalmente chamartp_finalize
viaPyObject_CallFinalizerFromDealloc()
se desejar reutilizar esse código para auxiliar na destruição de objetos. Isso é recomendado porque garante quetp_finalize
seja sempre chamado antes da destruição. Consulte a documentação detp_dealloc
para obter um exemplo de código.Se o objeto for membro de um isolado cíclico e
tp_clear
não conseguir interromper o ciclo de referência ou o isolado cíclico não for detectado (talvezgc.disable()
tenha sido chamado ou o sinalizadorPy_TPFLAGS_HAVE_GC
tenha sido omitido erroneamente em um dos tipos envolvidos), os objetos permanecerão indefinidamente não coletáveis (eles “vazam”). Vejagc.garbage
.
Se o objeto for marcado como compatível com coleta de lixo (o sinalizador Py_TPFLAGS_HAVE_GC
estiver definido em tp_flags
), os seguintes eventos também serão possíveis:
O coletor de lixo ocasionalmente chama
tp_traverse
para identificar isolados cíclicos.Quando o coletor de lixo descobre um isolado cíclico, ele finaliza um dos objetos do grupo marcando-o como finalizado e chamando sua função
tp_finalize
, se houver. Isso se repete até que o isolado cíclico não exista mais ou todos os objetos tenham sido finalizados.tp_finalize
tem permissão para ressuscitar o objeto adicionando uma referência externa ao isolado cíclico. A nova referência faz com que o grupo de objetos não forme mais um isolado cíclico (o ciclo de referência ainda pode existir, mas se existir, os objetos não estarão mais isolados).Quando o coletor de lixo descobre um isolado cíclico e todos os objetos do grupo já foram marcados como finalizados, o coletor de lixo limpa um ou mais objetos não limpos no grupo (possivelmente simultaneamente) chamando a função
tp_clear
de cada um. Isso se repete enquanto o isolado cíclico ainda existir e nem todos os objetos tiverem sido limpos.
Destruição de isolado cíclico¶
Abaixo estão listados os estágios de vida de um isolado cíclico hipotético que continua a existir após cada objeto membro ser finalizado ou limpo. É um vazamento de memória se um isolado cíclico passar por todos esses estágios; ele deve desaparecer assim que todos os objetos forem limpos, ou até mesmo antes. Um isolado cíclico pode desaparecer porque o ciclo de referência foi quebrado ou porque os objetos não estão mais isolados devido à ressurreição do finalizador (veja tp_finalize
).
Alcançável (ainda não é um isolado cíclico): Todos os objetos estão em seu estado normal e acessível. Um ciclo de referência pode existir, mas uma referência externa significa que os objetos ainda não estão isolados.
Inalcançável, mas consistente: A referência final de fora do grupo cíclico de objetos foi removida, causando o isolado dos objetos (criando assim um isolado cíclico). Nenhum dos objetos do grupo foi finalizado ou limpo ainda. O isolado cíclico permanece neste estágio até alguma execução futura do coletor de lixo (não necessariamente a próxima execução, pois a próxima execução pode não varrer todos os objetos).
Mistura de finalizados e não finalizados: Objetos em um isolado cíclico são finalizados um de cada vez, o que significa que há um período em que o isolado cíclico é composto por uma mistura de objetos finalizados e não finalizados. A ordem de finalização não é especificada, portanto, pode parecer aleatória. Um objeto finalizado deve se comportar de maneira sensata quando objetos não finalizados interagem com ele, e um objeto não finalizado deve ser capaz de tolerar a finalização de um subconjunto arbitrário de seus referentes.
Todos finalizados: todos os objetos em um isolado cíclico são finalizados antes que qualquer um deles seja limpo.
Combinação de finalizado e limpo: Os objetos podem ser limpos em série ou simultaneamente (mas com a GIL mantida); de qualquer forma, alguns serão concluídos antes de outros. Um objeto finalizado deve ser capaz de tolerar a limpeza de um subconjunto de seus referentes. PEP 442 chama essa etapa de “lixo cíclico”.
Vazamento: Se um isolado cíclico ainda existir após todos os objetos do grupo terem sido finalizados e limpos, os objetos permanecerão indefinidamente não coletáveis (consulte
gc.garbage
). É um bug se um isolado cíclico atingir esse estágio — significa que os métodostp_clear
dos objetos participantes falharam em interromper o ciclo de referência conforme necessário.
Se tp_clear
não existisse, o Python não teria como interromper com segurança um ciclo de referência. A simples destruição de um objeto em um isolado cíclico resultaria em um ponteiro pendente, desencadeando um comportamento indefinido quando um objeto que referencia o objeto destruído é destruído. A etapa de limpeza torna a destruição de objetos um processo de duas fases: primeiro, tp_clear
é chamado para destruir parcialmente os objetos o suficiente para desvinculá-los uns dos outros; em seguida, tp_dealloc
é chamado para completar a destruição.
Ao contrário da limpeza, a finalização não é uma fase da destruição. Um objeto finalizado ainda deve se comportar corretamente, continuando a cumprir seus contratos de design. O finalizador de um objeto pode executar código Python arbitrário e até mesmo impedir a destruição iminente adicionando uma referência. O finalizador está relacionado à destruição apenas pela ordem de chamada — se for executado, será executado antes da destruição, que começa com tp_clear
(se chamado) e termina com tp_dealloc
.
A etapa de finalização não é necessária para recuperar com segurança os objetos em um isolado cíclico, mas sua existência facilita o design de tipos que se comportam de maneira sensata quando os objetos são limpos. Limpar um objeto pode necessariamente deixá-lo em um estado quebrado, parcialmente destruído — pode ser inseguro chamar qualquer um dos métodos do objeto limpo ou acessar qualquer um de seus atributos. Com a finalização, apenas objetos finalizados podem interagir com objetos limpos; objetos não finalizados têm a garantia de interagir apenas com objetos não limpos (mas potencialmente finalizados).
Para resumir as interações possíveis:
Um objeto não finalizado pode ter referências a ou de objetos não finalizados e finalizados, mas não a ou de objetos limpos.
Um objeto finalizado pode ter referências a ou de objetos não finalizados, finalizados e limpos.
Um objeto limpo pode ter referências a ou de objetos finalizados e limpos, mas não a ou de objetos não finalizados.
Sem ciclos de referência, um objeto pode ser simplesmente destruído após a exclusão de sua última referência; as etapas de finalização e limpeza não são necessárias para recuperar objetos não utilizados com segurança. No entanto, pode ser útil chamar automaticamente tp_finalize
e tp_clear
antes da destruição, pois o design de tipos é simplificado quando todos os objetos sempre experimentam a mesma série de eventos, independentemente de terem participado ou não de um isolado cíclico. Atualmente, o Python chama tp_finalize
e tp_clear
apenas conforme necessário para destruir um isolado cíclico; isso pode mudar em uma versão futura.
Funções¶
Para alocar e liberar memória, consulte Alocando objetos na heap.
-
void PyObject_CallFinalizer(PyObject *op)¶
Finaliza o objeto conforme descrito em
tp_finalize
. Chame esta função (ouPyObject_CallFinalizerFromDealloc()
) em vez de chamartp_finalize
diretamente, pois esta função pode desduplicar várias chamadas paratp_finalize
. Atualmente, as chamadas são desduplicadas somente se o tipo oferecer suporte a coleta de lixo (ou seja, se o sinalizadorPy_TPFLAGS_HAVE_GC
estiver definido); isso pode mudar no futuro.
-
int PyObject_CallFinalizerFromDealloc(PyObject *op)¶
O mesmo que
PyObject_CallFinalizer()
, mas deve ser chamado no início do destrutor do objeto (tp_dealloc
). Não deve haver nenhuma referência ao objeto. Se o finalizador do objeto ressuscitar o objeto, esta função retornará -1; nenhuma outra destruição deverá ocorrer. Caso contrário, esta função retornará 0 e a destruição pode continuar normalmente.Ver também
tp_dealloc
para código de exemplo.