Quando você sai de um aplicativo C, a memória malloc-ed é automaticamente liberada?

91

Digamos que eu tenha o seguinte código C:

int main () {
  int *p = malloc(10 * sizeof *p);
  *p = 42;
  return 0;  //Exiting without freeing the allocated memory
}

Quando eu compilar e executar esse programa C, ou seja, depois de alocar algum espaço na memória, essa memória que aloquei ainda estará alocada (ou seja, basicamente ocupando espaço) após eu sair do aplicativo e o processo terminar?

Andreas Grech
fonte
9
é um "bom estilo" limpar sua memória, não porque você pode executar em um sistema operacional que não tem memória protegida (que é a principal sugestão abaixo), mas porque aumenta a probabilidade de você encontrar vazamentos de memória, e mantém seu código enxuto e correto ...
Matt Joiner

Respostas:

110

Depende do sistema operacional. A maioria dos sistemas operacionais modernos (e todos os principais) liberarão memória não liberada pelo programa quando ele terminar.

Confiar nisso é uma prática ruim e é melhor liberá-lo explicitamente. O problema não é apenas que seu código parece ruim. Você pode decidir que deseja integrar seu pequeno programa a um maior e de longa duração. Então, um pouco mais tarde, você terá que passar horas rastreando vazamentos de memória.
Depender de um recurso de um sistema operacional também torna o código menos portátil.

Yacoby
fonte
16
Certa vez, encontrei o win98 em uma plataforma embarcada e, com base nessa experiência, posso dizer que ele NÃO libera memória quando os programas fecham.
San Jacinto
8
@Ken foi um exemplo. Além disso, há uma linha entre YAGNI e codificação desleixada. Não liberar recursos atravessa-o. O princípio YAGNI também foi feito para ser aplicado a recursos, não a códigos que fazem o programa funcionar corretamente. (E não liberar memória é um bug).
Yacoby
5
+1: a coisa mais importante a se considerar é que o gerenciamento de memória é como Yacoby afirma muito corretamente: "um recurso do sistema operacional" . A menos que eu esteja enganado, a linguagem de programação não define o que acontece antes ou depois da execução do programa.
D.Shawley
9
Liberar memória manualmente leva mais tempo, consome mais código e apresenta a possibilidade de bugs (diga-me que você nunca viu um bug no código de desalocação!). Não é "negligente" omitir intencionalmente algo que é pior em todos os aspectos para o seu caso de uso específico. A menos ou até que você pretenda executá-lo em algum sistema antigo / minúsculo que não pode liberar páginas após o término do processo, ou integrá-lo em um programa maior (YAGNI), parece uma perda líquida para mim. Eu sei que dói no ego do programador pensar em não limpar você mesmo, mas de que maneira prática é realmente melhor?
Ken
6
Qualquer pessoa que proponha vazamentos de memória no SO deve ser despojada de toda a reputação e emblemas
ulterior
53

Em geral, os sistemas operacionais modernos de uso geral são limpos após processos encerrados . Isso é necessário porque a alternativa é o sistema perder recursos com o tempo e exigir a reinicialização devido a programas que são mal escritos ou simplesmente têm bugs que raramente ocorrem e vazam recursos.

Ter seu programa liberando explicitamente seus recursos de qualquer maneira pode ser uma boa prática por vários motivos, como:

  • Se você tiver recursos adicionais que não são limpos pelo sistema operacional na saída, como arquivos temporários ou qualquer tipo de mudança no estado de um recurso externo, você precisará de um código para lidar com todas essas coisas na saída, e isso geralmente é elegantemente combinado com a liberação de memória.
  • Se o seu programa começar a ter uma vida útil mais longa, você não desejará que a única maneira de liberar memória seja encerrando. Por exemplo, você pode querer converter seu programa em um servidor (daemon) que continua em execução enquanto trata de muitas solicitações de unidades individuais de trabalho, ou seu programa pode se tornar uma pequena parte de um programa maior.

No entanto, aqui está um motivo para pular a liberação de memória: desligamento eficiente . Por exemplo, suponha que seu aplicativo contenha um grande cache na memória. Se, ao sair, passar por toda a estrutura do cache e o liberar um pedaço de cada vez, isso não terá nenhuma utilidade e desperdiçará recursos. Especialmente, considere o caso em que as páginas de memória contendo seu cache foram trocadas para o disco pelo sistema operacional; percorrendo a estrutura e liberando-a, você está trazendo todas essas páginas de volta à memória de uma vez , desperdiçando tempo e energia significativos sem nenhum benefício real e possivelmente até fazendo com que outros programas no sistema sejam trocados!

Como exemplo relacionado, existem servidores de alto desempenho que funcionam criando um processo para cada solicitação e, em seguida, fazendo com que ele seja encerrado quando concluído; dessa forma, eles nem mesmo precisam rastrear a alocação de memória e nunca fazem nenhuma liberação ou coleta de lixo, uma vez que tudo simplesmente desaparece na memória livre do sistema operacional no final do processo. (O mesmo tipo de coisa pode ser feito dentro de um processo usando um alocador de memória personalizado, mas requer uma programação muito cuidadosa; essencialmente, fazer a própria noção de "processos leves" dentro do processo do sistema operacional.)

Kevin Reid
fonte
11

Minhas desculpas por postar tanto tempo após a última postagem neste tópico.

Um ponto adicional. Nem todos os programas chegam a saídas elegantes. Falhas e ctrl-Cs, etc. farão com que o programa seja encerrado de maneiras não controladas. Se seu sistema operacional não liberasse seu heap, limpe sua pilha, exclua variáveis ​​estáticas, etc, você eventualmente travaria seu sistema por vazamentos de memória ou coisa pior.

Além disso, o interessante é que trava / quebra no Ubuntu, e eu suspeito que todos os outros sistemas operacionais modernos, têm problemas com recursos "controlados". Sockets, arquivos, dispositivos, etc. podem permanecer "abertos" quando um programa termina / trava. também é uma boa prática fechar qualquer coisa com um "identificador" ou "descritor" como parte de sua limpeza antes da saída normal.

Atualmente, estou desenvolvendo um programa que usa muito soquetes. Quando eu fico preso em um travamento, tenho que ctrl-c para fora dele, assim, encalhando meus soquetes. Eu adicionei um std :: vector para coletar uma lista de todos os sockets abertos e um manipulador de sigaction que captura sigint e sigterm. O manipulador percorre a lista e fecha os soquetes. Estou pensando em fazer uma rotina de limpeza semelhante para usar antes de lançar, que levará ao encerramento prematuro.

Alguém se importa em comentar sobre este design?

Wes Miller
fonte
1
Fico feliz que você tenha dito isso, porque eu tinha um programa que deixava recursos de soquete disponíveis e nosso sistema Ubuntu precisava ser reiniciado a cada duas semanas ou a memória começou a se esgotar e há bastante memória. Não tenho certeza se os recursos do sistema serão destruídos se você se esquecer de limpá-los.
octopusgrabbus
8
Stackoverflow não é um fórum; não há nada de errado em responder a uma pergunta antiga. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12
6

O que está acontecendo aqui ( em um sistema operacional moderno ), é que seu programa é executado dentro de seu próprio "processo". Esta é uma entidade de sistema operacional dotada de seu próprio espaço de endereço, descritores de arquivo, etc. Suas mallocchamadas estão alocando memória do "heap" ou páginas de memória não alocadas que são atribuídas ao seu processo.

Quando seu programa termina, como neste exemplo, todos os recursos atribuídos ao seu processo são simplesmente reciclados / destruídos pelo sistema operacional. No caso da memória, todas as páginas de memória atribuídas a você são simplesmente marcadas como "livres" e recicladas para o uso de outros processos. As páginas são um conceito de nível inferior do que o que malloc manipula - como resultado, as especificações de malloc / free são simplesmente apagadas conforme a coisa toda é limpa.

É o equivalente moral de, quando você termina de usar seu laptop e quer dá-lo a um amigo, não se preocupa em excluir cada arquivo individualmente. Você acabou de formatar o disco rígido.

Dito isso, como todos os outros respondentes estão observando, confiar nisso não é uma boa prática:

  1. Você deve sempre programar para cuidar dos recursos, e em C isso significa memória também. Você pode acabar embutindo seu código em uma biblioteca, ou pode acabar rodando por muito mais tempo do que o esperado.
  2. Alguns sistemas operacionais (os mais antigos e talvez alguns integrados modernos) podem não manter esses limites rígidos de processo e suas alocações podem afetar os espaços de endereço de outros.
Ben Zotto
fonte
4

Sim. O sistema operacional limpa os recursos. Bem ... as versões antigas do NetWare não.

Edit: Como San Jacinto apontou, certamente existem sistemas (além do NetWare) que não fazem isso. Mesmo em programas descartáveis, tento criar o hábito de liberar todos os recursos apenas para manter o hábito.

Mark Wilkins
fonte
3
Não estou downvoting, mas esta é uma postagem de bastante perigosa para a posteridade. O DOS ainda é usado em muitas plataformas embarcadas, e duvido SERIAMENTE que faça a limpeza da memória para você. A ampla generalização está errada.
San Jacinto
@San Jacinto: Esse é um bom ponto. É por isso que fiz a referência ao NetWare, mas provavelmente poderia ser necessário um esclarecimento. Vou editar um pouco.
Mark Wilkins
3
@San DOS não é um sistema operacional multitarefa - quando um programa DOS (excluindo TSRs) termina, toda a memória fica disponível para o próximo programa a ser carregado.
@Neil obrigado pelo lembrete, mas eu estava me referindo a um programa semelhante ao TSR que seria iniciado quando um evento ocorresse, como é um uso comum para sistemas embarcados. No entanto, obrigado por sua experiência e esclarecimento onde eu falhei :)
San Jacinto
2

Sim, o sistema operacional libera toda a memória quando o processo termina.

Draemon
fonte
Não vejo por que isso foi rejeitado. a memória malloced será liberada quando o processo morrer (a definição da wikipedia de malloc diz isso)
Arve
7
A Wikipedia não é o manual de todos os sistemas operacionais existentes. A maioria dos sistemas operacionais modernos irá recuperar a memória, mas nem todos (e particularmente nem todos os antigos) o fazem. Adicione a isso, mallocsó posso prometer o que C fará com a memória; por design, C não garante muito em relação ao comportamento fora do próprio C. Se o aplicativo morrer inesperadamente, qualquer promessa feita pela biblioteca de tempo de execução será nula e sem efeito, uma vez que ela não está mais viva para cumpri-la.
cHao
2

Depende, os sistemas operacionais geralmente limparão para você, mas se você estiver trabalhando, por exemplo, em um software integrado, ele pode não ser lançado.

Apenas certifique-se de liberá-lo, ele pode economizar muito tempo depois, quando você quiser integrá-lo a um grande projeto.

Charles
fonte
0

Isso realmente depende do sistema operacional, mas para todos os sistemas operacionais que você encontrar, a alocação de memória desaparecerá quando o processo for encerrado.


fonte
0

Acho que a liberação direta é melhor. Comportamento indefinido é a pior coisa, então se você tiver acesso enquanto ele ainda está definido em seu processo, faça-o, pois existem muitos bons motivos que as pessoas deram para isso.

Quanto a onde, ou se, descobri que em W98, a verdadeira questão era 'quando' (não vi uma postagem enfatizando isso). Um pequeno programa de modelo (para entrada MIDI SysEx, usando vários espaços malloc) iria liberar memória no bit WM_DESTROY do WndProc, mas quando eu transplantei isso para um programa maior, ele travou ao sair. Presumi que isso significava que estava tentando liberar o que o sistema operacional já havia liberado durante uma limpeza maior. Se eu fizesse isso em WM_CLOSE e depois chamasse DestroyWindow (), tudo funcionaria bem, saída limpa instantânea.

Embora não seja exatamente o mesmo que os buffers de MIDI, há semelhanças no fato de que é melhor manter o processo intacto, limpar totalmente e depois sair. Com pedaços de memória modestos, isso é muito rápido. Descobri que muitos buffers pequenos trabalharam mais rápido na operação e limpeza do que menos grandes.

Exceções podem existir, como alguém disse ao evitar puxar grandes pedaços de memória de um arquivo de troca no disco, mas mesmo isso pode ser minimizado mantendo mais e menores espaços alocados.


fonte