eryksun respondeu à pergunta 1 e eu respondi à pergunta 3 (o original 4), mas agora vamos responder à pergunta 2:
Por que ele libera 50.5mb em particular - qual é a quantidade lançada com base?
O que se baseia é, finalmente, toda uma série de coincidências dentro do Python e malloc
que são muito difíceis de prever.
Primeiro, dependendo de como você está medindo a memória, você pode estar medindo apenas páginas realmente mapeadas na memória. Nesse caso, sempre que uma página for trocada pelo pager, a memória aparecerá como "liberada", mesmo que não tenha sido liberada.
Ou você pode medir páginas em uso, que podem ou não contar páginas alocadas, mas nunca tocadas (em sistemas que otimizam demais a alocação, como linux), páginas alocadas, mas marcadas MADV_FREE
, etc.
Se você realmente está avaliando as páginas alocadas (o que na verdade não é uma coisa muito útil, mas parece ser o que você está perguntando), e as páginas foram realmente desalocadas, há duas circunstâncias em que isso pode acontecer: você usou brk
ou equivalente para reduzir o segmento de dados (muito raro hoje em dia) ou você usoumunmap
ou semelhante para liberar um segmento mapeado. (Também há, teoricamente, uma variante menor do último, pois há maneiras de liberar parte de um segmento mapeado - por exemplo, roube-o MAP_FIXED
para um MADV_FREE
segmento que você imediatamente mapeia.)
Mas a maioria dos programas não aloca coisas diretamente das páginas da memória; eles usam ummalloc
alocador de estilo. Quando você liga free
, o alocador só pode liberar páginas para o sistema operacional se você estiver free
no último objeto ativo em um mapeamento (ou nas últimas N páginas do segmento de dados). Não há como seu aplicativo prever isso razoavelmente ou até detectar que isso aconteceu com antecedência.
O CPython torna isso ainda mais complicado - ele possui um alocador de objetos personalizado de dois níveis em cima de um alocador de memória personalizado malloc
. (Vejo os comentários da fonte para obter uma explicação mais detalhada.) Além disso, mesmo no nível da API C, muito menos no Python, você nem controla diretamente quando os objetos de nível superior são desalocados.
Então, quando você libera um objeto, como você sabe se ele libera memória para o sistema operacional? Bem, primeiro você precisa saber que lançou a última referência (incluindo todas as referências internas desconhecidas), permitindo que o GC a desaloque. (Diferentemente de outras implementações, pelo menos o CPython desalocará um objeto assim que permitido.) Isso geralmente desaloca pelo menos duas coisas no próximo nível abaixo (por exemplo, para uma string, você está liberando o PyString
objeto e o buffer da string )
Se vocês fazer desalocar um objeto, para saber se isso faz com que a próxima baixo nível para desalocar um bloco de armazenamento de objetos, você tem que saber o estado interno do objeto alocador, bem como a forma como ele é implementado. (Obviamente, isso não pode acontecer, a menos que você esteja desalocando a última coisa no bloco e, mesmo assim, pode não acontecer.)
Se você fazer desalocar um bloco de armazenamento de objetos, para saber se isso faz com que uma free
chamada, você tem que saber o estado interno do alocador PyMem, bem como a forma como ele é implementado. (Novamente, você deve desalocar o último bloco em uso dentro de ummalloc
região ed e, mesmo assim, isso pode não acontecer.)
Se você faz free
uma malloc
região ed, para saber se isso causa um munmap
ou equivalente (ou brk
), você precisa conhecer o estado interno do malloc
e também como ele é implementado. E este, diferentemente dos outros, é altamente específico da plataforma. (E, novamente, você geralmente precisa desalocar o último em uso malloc
em um mmap
segmento e, mesmo assim, isso pode não acontecer.)
Portanto, se você quiser entender por que lançou exatamente 50,5mb, precisará rastrear de baixo para cima. Por que malloc
desmapear 50.5mb no valor de páginas quando você fez uma ou mais free
chamadas (por provavelmente um pouco mais que 50.5mb)? Você precisaria ler a plataforma malloc
e percorrer as várias tabelas e listas para ver seu estado atual. (Em algumas plataformas, pode até fazer uso de informações no nível do sistema, o que é praticamente impossível de capturar sem fazer uma captura instantânea do sistema para inspecionar offline, mas felizmente isso geralmente não é um problema.) E então você precisa faça a mesma coisa nos 3 níveis acima disso.
Portanto, a única resposta útil para a pergunta é "Porque".
A menos que você esteja desenvolvendo recursos limitados (por exemplo, incorporados), não há motivos para se preocupar com esses detalhes.
E se você estiver desenvolvendo recursos limitados, conhecer esses detalhes é inútil; você praticamente precisa executar uma execução final em todos esses níveis e, especificamente, mmap
na memória necessária no nível do aplicativo (possivelmente com um alocador de zona específico do aplicativo, simples e bem compreendido).