Introdução
Gestão de memória é o processo de alocação, desalocação e coordenação eficiente da memória para que todos os diferentes processos funcionem sem problemas e possam acessar de forma otimizada os diferentes recursos do sistema. O gerenciamento de memória também envolve a limpeza da memória de objetos que não estão mais sendo acessados.
Em Python, o gerenciador de memória é responsável por este tipo de tarefas, executando periodicamente para limpar, alocar e gerenciar a memória. Ao contrário de C, Java e outras linguagens de programação, o Python gerencia os objetos usando contagem de referência. Isto significa que o gerenciador de memória mantém o controle do número de referências para cada objeto no programa. Quando a contagem de referência de um objeto cai a zero, o que significa que o objeto não está mais sendo usado, o coletor de lixo (parte do gerenciador de memória) libera automaticamente a memória daquele objeto em particular.
O usuário não precisa se preocupar com o gerenciamento de memória, pois o processo de alocação e desalocação de memória é totalmente automático. A memória recuperada pode ser usada por outros objetos.
Python Garbage Collection
Como explicado anteriormente, Python elimina objetos que não são mais referenciados no programa para liberar espaço de memória. Este processo no qual Python libera blocos de memória que não são mais usados é chamado de Coleta de Lixo. O Coletor de Lixo Python (GC) é executado durante a execução do programa e é acionado se a contagem de referência for reduzida a zero. A contagem de referência aumenta se um objeto recebe um novo nome ou é colocado em um recipiente, como um tuple ou dicionário. Da mesma forma, a contagem de referência diminui quando a referência a um objeto é reatribuída, quando a referência do objeto sai do escopo, ou quando um objeto é apagado.
A memória é uma pilha que contém objetos e outras estruturas de dados usadas no programa. A alocação e desalocação desse espaço de pilha é controlada pelo gerenciador de memória Python através do uso de funções API.
Python Objects in Memory
A cada variável em Python atua como um objeto. Os objetos podem ser simples (contendo números, strings, etc.) ou containers (dicionários, listas, ou classes definidas pelo usuário). Além disso, Python é uma linguagem tipada dinamicamente, o que significa que não precisamos declarar as variáveis ou seus tipos antes de usá-los em um programa.
Por exemplo:
Se você olhar para as 2 primeiras linhas do programa acima, o objeto x
é conhecido. Quando apagamos o objeto x
e tentamos usá-lo, obtemos um erro afirmando que a variável x
não está definida.
Você pode ver que a coleta de lixo em Python é totalmente automatizada e o programador não precisa se preocupar com isso, ao contrário de linguagens como C.
Modificando o Colector de Lixo
O Colector de Lixo Python tem três gerações nas quais os objetos são classificados. Um novo objeto no ponto inicial do seu ciclo de vida é a primeira geração do coletor de lixo. Como o objeto sobrevive à coleta de lixo, ele será movido para as próximas gerações. Cada uma das 3 gerações do coletor de lixo tem um limite. Especificamente, quando o limiar do número de alocações menos o número de alocações é excedido, essa geração fará a coleta de lixo.
Gerações mais antigas também são coletadas mais vezes do que as gerações mais altas. Isto porque os objetos mais novos são mais propensos a serem descartados do que os objetos antigos.
O módulo gc
inclui funções para alterar o valor limiar, acionar um processo de coleta de lixo manualmente, desativar o processo de coleta de lixo, etc. Podemos verificar os valores limiares das diferentes gerações do coletor de lixo usando o método get_threshold()
:
import gcprint(gc.get_threshold())
Sample Output:
(700, 10, 10)
Como você vê, aqui temos um limiar de 700 para a primeira geração, e 10 para cada uma das outras duas gerações.
Podemos alterar o valor limiar para acionar o processo de coleta de lixo usando o método set_threshold()
do módulo gc
:
gc.set_threshold(900, 15, 15)
No exemplo acima, aumentamos o valor limiar para todas as 3 gerações. Aumentar o valor limite irá diminuir a frequência de funcionamento do coletor de lixo. Normalmente, não precisamos pensar muito na coleta de lixo do Python como desenvolvedor, mas isto pode ser útil ao otimizar o tempo de execução do Python para o seu sistema alvo. Um dos principais benefícios é que o mecanismo de coleta de lixo do Python lida com muitos detalhes de baixo nível para o desenvolvedor automaticamente.
Por que executar a coleta manual de lixo?
Sabemos que o interpretador Python mantém um registro de referências a objetos usados em um programa. Em versões anteriores do Python (até a versão 1.6), o interpretador Python usava apenas o mecanismo de contagem de referências para lidar com a memória. Quando a contagem de referências cai para zero, o Python interpreter libera automaticamente a memória. Este mecanismo clássico de contagem de referência é muito eficaz, exceto que ele não funciona quando o programa tem ciclos de referência. Um ciclo de referência acontece se um ou mais objetos são referenciados uns aos outros, e portanto a contagem de referência nunca chega a zero.
Vamos considerar um exemplo.
>>> def create_cycle():... list = ... list.append(list)... return list... >>> create_cycle()]
O código acima cria um ciclo de referência, onde o objeto list
se refere a si mesmo. Portanto, a memória para o objeto list
não será liberada automaticamente quando a função retornar. O problema do ciclo de referência não pode ser resolvido através da contagem de referência. Entretanto, este problema do ciclo de referência pode ser resolvido alterando o comportamento do coletor de lixo em sua aplicação Python.
Para isso, podemos usar a função gc.collect()
do módulo gc
.
import gcn = gc.collect()print("Number of unreachable objects collected by GC:", n)
O gc.collect()
retorna o número de objetos que ele coletou e desalocou.
Há duas formas de realizar a coleta manual de lixo: a coleta de lixo baseada em tempo ou em eventos.
A coleta de lixo baseada em tempo é bastante simples: a função gc.collect()
é chamada após um intervalo de tempo fixo.
A coleta de lixo baseada em eventos chama a função gc.collect()
após a ocorrência de um evento (isto é, quando a aplicação é encerrada ou a aplicação permanece ociosa por um período de tempo específico).
Vamos entender o trabalho de coleta manual de lixo criando alguns ciclos de referência.
A saída é como abaixo:
Creating garbage...Collecting...Number of unreachable objects collected by GC: 8Uncollectable garbage:
O script acima cria um objeto de lista que é referenciado por uma variável, criativamente chamada list
. O primeiro elemento do objeto de lista se refere a si mesmo. A contagem de referência do objeto list é sempre maior que zero mesmo que ele seja excluído ou fora do escopo do programa. Portanto, o objeto list
não é lixo coletado devido à referência circular. O mecanismo de coleta de lixo em Python irá verificar automaticamente e coletar referências circulares periodicamente.
No código acima, como a contagem de referência é pelo menos 1 e nunca pode chegar a 0, nós temos o lixo coletado forçosamente chamando gc.collect()
. No entanto, lembre-se de não forçar a coleta de lixo com freqüência. A razão é que mesmo depois de liberar a memória, o GC leva tempo para avaliar a elegibilidade do objeto a ser coletado, ocupando tempo e recursos do processador. Além disso, lembre-se de gerenciar manualmente o coletor de lixo somente após o seu aplicativo ter iniciado completamente.
Conclusion
Neste artigo, discutimos como o gerenciamento de memória em Python é tratado automaticamente usando contagem de referência e estratégias de coleta de lixo. Sem a coleta de lixo, implementar um mecanismo de gerenciamento de memória bem sucedido em Python é impossível. Além disso, os programadores não precisam se preocupar em excluir a memória alocada, pois ela é cuidada pelo gerenciador de memória Python. Isto leva a menos vazamentos de memória e a um melhor desempenho.