Coleta de lixo Python

Resumo : neste tutorial, você aprenderá como funciona a coleta de lixo do Python e como interagir com o coletor de lixo programaticamente.

Introdução à coleta de lixo Python

Em C/C++, você é totalmente responsável por gerenciar a memória do programa. No entanto, em Python, você não precisa gerenciar a memória sozinho porque o Python faz isso automaticamente.

No tutorial de referências , você aprendeu que o Python Memory Manager monitora referências de objetos. O Memory Manager destrói o objeto e recupera a memória quando a contagem de referência desse objeto chega a zero.

No entanto, a contagem de referência nem sempre funciona corretamente. Por exemplo, quando você tem um objeto que faz referência a si mesmo ou dois objetos fazem referência um ao outro. Isso cria algo chamado referências circulares.

Quando o Python Memory Manager não consegue remover objetos com referências circulares, ocorre um vazamento de memória.

É por isso que o coletor de lixo entra em ação para corrigir as referências circulares.

Python permite que você interaja com o coletor de lixo por meio do módulo integrado gc.

Interagindo com o coletor de lixo Python

Neste exemplo, primeiro criaremos uma referência circular entre duas instâncias da classe A e da classe B. Em seguida, usaremos o coletor de lixo para destruir os objetos na referência circular.

Primeiro, importe os módulos gce ctypese defina duas funções para contar referências e verificar se existe um objeto na memória:

import gc
import ctypes


def ref_count(address):
    return ctypes.c_long.from_address(address).value


def object_exists(object_id):
    for object in gc.get_objects():
        if id(object) == object_id:
            return True

    return False
Linguagem de código:  Python  ( python )

Neste código, ref_count()retorna a contagem de referências de um objeto especificado por seu endereço de memória. E a object_exists()função retorna Truese existir um objeto na memória.

Segundo, crie duas classes A e Bque tenham uma referência entre si:

class A:
    def __init__(self):
        self.b = B(self)
        print(f'A: {hex(id(self))}, B: {hex(id(self.b))}')


class B:
    def __init__(self, a):
        self.a = a
        print(f'B: {hex(id(self))}, A: {hex(id(self.a))}')
Linguagem de código:  Python  ( python )

Terceiro, desative o coletor de lixo chamando a disable()função:

gc.disable()Linguagem de código:  Python  ( python )

Quarto, crie uma nova instância da classe A que também crie automaticamente uma nova instância da classe B:

a = A()Linguagem de código:  Python  ( python )

Saída:

B: 0x20fccd148e0, A: 0x20fccd75c40
A: 0x20fccd75c40, B: 0x20fccd148e0Linguagem de código:  Python  ( python )

Quinto, defina duas variáveis ​​para armazenar os endereços de memória das instâncias de A e B. Essas variáveis ​​controlam os endereços de memória das instâncias de A e B quando a variável afaz referência a outro objeto.

a_id = id(a)
b_id = id(a.b)Linguagem de código:  Python  ( python )

Sexto, mostre as contagens de referência das instâncias de A e B:

print(ref_count(a_id))  # 2
print(ref_count(b_id))  # 1Linguagem de código:  Python  ( python )

A instância de A tem duas referências que é a variável ae a instância de B. E a instância de B tem uma referência que é a instância de A.

Sétimo, verifique se ambas as instâncias de A e B estão na memória:

print(object_exists(a_id))  # True
print(object_exists(b_id))  # TrueLinguagem de código:  Python  ( python )

Ambos existem.

Oitavo, defina uma variável para None:

a = NoneLinguagem de código:  Python  ( python )

Nono, obtenha as contagens de referência da instância de A e B:

print(ref_count(a_id))  # 1
print(ref_count(b_id))  # 1Linguagem de código:  Python  ( python )

Agora, ambas as contagens de referência da instância de A e B são 1.

Décimo, verifique se as instâncias existem:

print(object_exists(a_id))  # True
print(object_exists(b_id))  # TrueLinguagem de código:  Python  ( python )

Ambos ainda existem como esperado.

Décimo primeiro, inicie o coletor de lixo:

gc.collect()Linguagem de código:  Python  ( python )

Quando o coletor de lixo é executado, ele pode detectar a referência circular, destruir os objetos e recuperar a memória.

Décimo sétimo, verifique se as instâncias de A e B existem:

print(object_exists(a_id))  # False
print(object_exists(b_id))  # FalseLinguagem de código:  Python  ( python )

Ambos não existem mais devido ao coletor de lixo.

Décimo terceiro, obtenha as contagens de referência das instâncias de A e B:

print(ref_count(a_id))  # 0
print(ref_count(b_id))  # 0Linguagem de código:  Python  ( python )

Junte tudo.

import gc
import ctypes


def ref_count(address):
    return ctypes.c_long.from_address(address).value


def object_exists(object_id):
    for object in gc.get_objects():
        if id(object) == object_id:
            return True

    return False


class A:
    def __init__(self):
        self.b = B(self)
        print(f'A: {hex(id(self))}, B: {hex(id(self.b))}')


class B:
    def __init__(self, a):
        self.a = a
        print(f'B: {hex(id(self))}, A: {hex(id(self.a))}')


# disable the garbage collector
gc.disable()

a = A()

a_id = id(a)
b_id = id(a.b)

print(ref_count(a_id))  # 2
print(ref_count(b_id))  # 1

print(object_exists(a_id))  # True
print(object_exists(b_id))  # True


a = None
print(ref_count(a_id))  # 1
print(ref_count(b_id))  # 1

print(object_exists(a_id))  # True
print(object_exists(b_id))  # True

# run the garbage collector
gc.collect()

# check if object exists
print(object_exists(a_id))  # False
print(object_exists(b_id))  # False

# reference count
print(ref_count(a_id))  # 0
print(ref_count(b_id))  # 0
Linguagem de código:  Python  ( python )

Resumo

  • Python gerencia automaticamente a memória para você usando contagem de referência e coletor de lixo.
  • O coletor de lixo Python ajuda a evitar vazamentos de memória, detectando referências circulares e destruindo objetos de maneira adequada.
  • Nunca desative o coletor de lixo, a menos que tenha um bom motivo para fazê-lo.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *