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 gc
e ctypes
e 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 True
se existir um objeto na memória.
Segundo, crie duas classes A
e B
que 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: 0x20fccd148e0
Linguagem 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 a
faz 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)) # 1
Linguagem de código: Python ( python )
A instância de A tem duas referências que é a variável a
e 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)) # True
Linguagem de código: Python ( python )
Ambos existem.
Oitavo, defina uma variável para None
:
a = None
Linguagem 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)) # 1
Linguagem 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)) # True
Linguagem 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)) # False
Linguagem 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)) # 0
Linguagem 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.