Em muitos livros e tutoriais, eu ouvi a prática do gerenciamento de memória enfatizar e senti que algumas coisas misteriosas e terríveis aconteceriam se eu não liberasse memória depois de terminar de usá-lo.
Não posso falar por outros sistemas (embora, para mim, seja razoável supor que eles adotem uma prática semelhante), mas pelo menos no Windows, o Kernel basicamente garante a limpeza da maioria dos recursos (com exceção de alguns poucos) usados pelo um programa após o término do programa. O que inclui memória de pilha, entre várias outras coisas.
Entendo por que você deseja fechar um arquivo depois de usá-lo para disponibilizá-lo ao usuário ou por que você deseja desconectar um soquete conectado a um servidor para economizar largura de banda, mas parece bobagem você precisa gerenciar toda a sua memória usada pelo seu programa.
Agora, eu concordo que essa questão é ampla, pois como você deve lidar com sua memória se baseia em quanta memória você precisa e quando precisa, então restringirei o escopo desta pergunta a este: Se eu precisar usar um pedaço de memória durante toda a vida útil do meu programa, é realmente necessário liberá-lo imediatamente antes do término do programa?
Editar: A pergunta sugerida como duplicada era específica da família de sistemas operacionais Unix. Sua resposta principal chegou a especificar uma ferramenta específica para Linux (por exemplo, Valgrind). Esta pergunta pretende abranger a maioria dos sistemas operacionais não incorporados "normais" e por que é ou não uma boa prática liberar memória necessária durante toda a vida útil de um programa.
fonte
Respostas:
Não é obrigatório, mas pode ter benefícios (além de algumas desvantagens).
Se o programa alocar memória uma vez durante o tempo de execução e, de outra forma, nunca a liberar até o término do processo, pode ser uma abordagem sensata não liberar a memória manualmente e confiar no sistema operacional. Em todos os sistemas operacionais modernos que conheço, isso é seguro; no final do processo, toda a memória alocada é retornada com segurança ao sistema.
Em alguns casos, a não limpeza explícita da memória alocada pode ser notavelmente mais rápida do que a limpeza.
No entanto, liberando explicitamente toda a memória no final da execução,
A vida útil dos programas pode mudar. Talvez seu programa seja um pequeno utilitário de linha de comando hoje, com uma vida útil típica de menos de 10 minutos, e aloque memória em partes de alguns kb a cada 10 segundos - portanto, não é necessário liberar nenhuma memória alocada antes que o programa termine. Posteriormente, o programa é alterado e obtém um uso prolongado como parte de um processo do servidor com uma vida útil de várias semanas - portanto, não liberar memória não utilizada no meio não é mais uma opção; caso contrário, o programa começará a consumir toda a memória disponível do servidor ao longo do tempo . Isso significa que você precisará revisar todo o programa e adicionar o código de desalocação posteriormente. Se você tiver sorte, essa é uma tarefa fácil; caso contrário, pode ser tão difícil que há grandes chances de você perder um lugar. E quando você estiver nessa situação, desejará ter adicionado o "free"
De maneira mais geral, escrever códigos de alocação e desalocação relacionados sempre conta em pares como um "bom hábito" entre muitos programadores: fazendo isso sempre, você diminui a probabilidade de esquecer o código de desalocação em situações em que a memória deve ser liberada.
fonte
main
.Liberar memória no final de uma execução de programas é apenas uma perda de tempo da CPU. É como arrumar uma casa antes de retirá-la da órbita.
No entanto, às vezes, o que era um programa de execução curta pode se tornar parte de um programa de execução muito mais longo. Então, liberar coisas se torna necessário. Se isso não foi pensado, pelo menos até certo ponto, pode envolver retrabalho substancial.
Uma solução inteligente para isso é o "talloc", que permite fazer uma grande quantidade de alocações de memória e depois jogar tudo fora com uma única chamada.
fonte
free
geralmente é muito mais rápido quemalloc
.Você pode usar uma linguagem com coleta de lixo (como Scheme, Ocaml, Haskell, Common Lisp e até Java, Scala, Clojure).
(Na maioria das linguagens GC-ed, não há nenhuma maneira para explicitamente e manualmente memória livre Às vezes, alguns valores pode ser! Finalizado , por exemplo, o GC eo sistema de execução fecharia um valor identificador de arquivo quando esse valor é inacessível, mas isso não é certo, não confiável, e você deve fechar explicitamente seus identificadores de arquivo, pois a finalização nunca é garantida)
Você também pode, para o seu programa codificado em C (ou mesmo C ++), usar o coletor de lixo conservador da Boehm . Você substituiria todo o seu
malloc
porGC_malloc
e não se incomodaria emfree
usar qualquer ponteiro. Claro que você precisa entender os prós e contras do uso do GC da Boehm. Leia também o manual do GC .Gerenciamento de memória é uma propriedade global de um programa. De alguma forma, ele (e a vivacidade de alguns dados) é não-composicional e não-modular, pois é uma propriedade inteira do programa.
Por fim, como outros apontaram, a utilização explícita da
free
zona de memória C alocada ao heap é uma boa prática. Para um programa de brinquedo que não aloca muita memória, você pode até decidir não terfree
memória (já que, quando o processo terminar, seus recursos, incluindo o espaço de endereço virtual , serão liberados pelo sistema operacional).Não, você não precisa fazer isso. E muitos programas do mundo real não se preocupam em liberar alguma memória necessária durante toda a vida útil (em particular, o compilador GCC não está liberando parte de sua memória). Entretanto, quando você faz isso (por exemplo, você não se incomoda em
free
usar uma parte específica de dados alocados dinamicamente em C ), é melhor comentar esse fato para facilitar o trabalho de futuros programadores no mesmo projeto. Costumo recomendar que a quantidade de memória não-liberada permaneça limitada e, geralmente, relativamente pequena para total da memória heap usada.Observe que o sistema
free
geralmente não libera memória para o sistema operacional (por exemplo, chamando munmap (2) em sistemas POSIX), mas geralmente marca uma zona de memória como reutilizável no futuromalloc
. Em particular, o espaço de endereço virtual (por exemplo, como visto/proc/self/maps
no Linux, consulte proc (5) ....) pode não diminuir depoisfree
(portanto, utilitários gostamps
outop
estão relatando a mesma quantidade de memória usada para o seu processo).fonte
Não é necessário , pois você não falhará em executar seu programa corretamente se não conseguir. No entanto, há razões pelas quais você pode optar, se tiver uma chance.
Um dos casos mais poderosos com os quais me deparo (repetidamente) é que alguém escreve um pequeno pedaço de código de simulação que é executado em seu executável. Eles dizem "gostaríamos que esse código fosse integrado à simulação". Depois, pergunto a eles como planejam reinicializar entre as corridas de monte carlo, e eles me olham sem entender. "O que você quer dizer com reinicializar? Você acabou de executar o programa com novas configurações?"
Às vezes, uma limpeza limpa facilita muito o uso do seu software. No caso de muitos exemplos, você presume que nunca precisa limpar algo e faz suposições sobre como pode lidar com os dados e sua vida útil em torno dessas suposições. Quando você muda para um novo ambiente em que essas presunções não são válidas, algoritmos inteiros podem não funcionar mais.
Para um exemplo de como as coisas podem ficar estranhas, veja como as linguagens gerenciadas lidam com a finalização no final dos processos ou como o C # lida com a interrupção programática dos domínios de aplicativos. Eles se prendem a nós porque há suposições que caem nas frestas nesses casos extremos.
fonte
Ignorando idiomas nos quais você não libera memória manualmente de qualquer maneira ...
O que você considera um "programa" no momento pode se tornar apenas uma função ou método que faz parte de um programa maior. E essa função pode ser chamada várias vezes. E a memória que você deveria ter "liberado manualmente" será um vazamento de memória. É claro que isso é um julgamento.
fonte
Como é muito provável que em algum momento você queira alterar seu programa, talvez o integre a outra coisa, execute várias instâncias em sequência ou em paralelo. Então será necessário liberar manualmente essa memória - mas você não lembrará mais das circunstâncias e custará muito mais tempo para re-entender seu programa.
Faça as coisas enquanto sua compreensão sobre elas ainda estiver fresca.
É um pequeno investimento que pode render grandes retornos no futuro.
fonte
Não, não é necessário, mas é uma boa ideia.
Você diz que "coisas misteriosas e terríveis aconteceriam se eu não liberasse memória depois de terminar de usá-la".
Em termos técnicos, a única conseqüência disso é que seu programa continuará consumindo mais memória até que um limite rígido seja alcançado (por exemplo, seu espaço de endereço virtual esteja esgotado) ou o desempenho se torne inaceitável. Se o programa está prestes a sair, nada disso importa porque o processo efetivamente deixa de existir. As "coisas misteriosas e terríveis" são puramente sobre o estado mental do desenvolvedor. Encontrar a fonte de um vazamento de memória pode ser um pesadelo absoluto (isso é um eufemismo) e é preciso muita habilidade e disciplina para escrever um código sem vazamentos. A abordagem recomendada para o desenvolvimento dessa habilidade e disciplina é sempre liberar memória quando ela não for mais necessária, mesmo que o programa esteja prestes a terminar.
Obviamente, isso tem a vantagem adicional de que seu código possa ser reutilizado e adaptado com mais facilidade, como outros já disseram.
No entanto , há pelo menos um caso em que é melhor não liberar memória imediatamente antes do encerramento do programa.
Considere o caso em que você fez milhões de pequenas alocações e elas foram trocadas principalmente para o disco. Quando você começa a liberar tudo, a maior parte da sua memória precisa ser trocada novamente para a RAM, para que as informações da contabilidade possam ser acessadas, apenas para descartar imediatamente os dados. Isso pode fazer com que o programa demore alguns minutos apenas para sair! Sem mencionar colocar muita pressão no disco e na memória física durante esse período. Se a memória física é escassa para começar (talvez o programa esteja sendo fechado porque outro programa está consumindo muita memória), é possível que as páginas individuais precisem ser trocadas várias vezes quando vários objetos precisam ser liberados do mesma página, mas não são liberados consecutivamente.
Se o programa for interrompido, o sistema operacional simplesmente descartará toda a memória que foi trocada para o disco, o que é quase instantâneo, pois não requer nenhum acesso ao disco.
É importante observar que, em uma linguagem OO, chamar o destruidor de um objeto também forçará a troca de memória; se você precisar fazer isso, poderá liberar a memória.
fonte
Os principais motivos para a limpeza manual são: é menos provável que você confunda um vazamento de memória genuíno com um bloco de memória que será limpo apenas na saída; às vezes, você encontrará bugs no alocador apenas quando percorrer toda a estrutura de dados para desalocar-lo, e você vai ter uma dor de cabeça muito menor se você refatorar para que algum objeto não precisa se desalocada antes de sair do programa.
Os principais motivos para não fazer a limpeza manual são: desempenho, desempenho, desempenho e a possibilidade de algum tipo de erro duas vezes livre ou usar após livre em seu código de limpeza desnecessário, que pode se transformar em um bug ou segurança de falha explorar.
Se você se preocupa com o desempenho, sempre deseja criar um perfil para descobrir onde está perdendo todo o seu tempo, em vez de adivinhar. Um possível comprometimento é agrupar seu código de limpeza opcional em um bloco condicional, deixá-lo na depuração para obter os benefícios de escrever e depurar o código e, em seguida, se e somente se você tiver determinado empiricamente que é demais sobrecarga, diga ao compilador para ignorá-lo no seu executável final. E um atraso no fechamento do programa é, quase por definição, quase nunca no caminho crítico.
fonte