O que REALMENTE acontece quando você não libera após o malloc?

538

Isso tem sido algo que me incomoda há muito tempo.

Todos nós somos ensinados na escola (pelo menos eu fui) que você DEVE libertar todos os indicadores que estão alocados. Estou um pouco curioso, porém, sobre o custo real de não liberar memória. Em alguns casos óbvios, como quando mallocé chamado dentro de um loop ou parte de uma execução de encadeamento, é muito importante liberar para que não haja vazamentos de memória. Mas considere os dois exemplos a seguir:

Primeiro, se eu tiver código, é algo como isto:

int main()
{
    char *a = malloc(1024);
    /* Do some arbitrary stuff with 'a' (no alloc functions) */
    return 0;
}

Qual é o resultado real aqui? Meu pensamento é que o processo morre e, em seguida, o espaço do heap se esgota, de modo que não há mal em perder a chamada free(no entanto, reconheço a importância de tê-lo de qualquer maneira para fechamento, manutenção e boas práticas). Estou certo nesse pensamento?

Segundo, digamos que eu tenho um programa que funciona um pouco como um shell. Os usuários podem declarar variáveis ​​como aaa = 123e essas são armazenadas em alguma estrutura de dados dinâmica para uso posterior. Claramente, parece óbvio que você usaria alguma solução que chamaria alguma função * assign (hashmap, lista vinculada, algo assim). Para esse tipo de programa, não faz sentido nunca ser liberado após a chamada, mallocporque essas variáveis ​​devem estar presentes o tempo todo durante a execução do programa e não há uma boa maneira (que eu possa ver) de implementá-lo com espaço alocado estaticamente. É um projeto ruim ter um monte de memória alocada, mas liberada apenas como parte do processo que termina? Se sim, qual é a alternativa?

Scott
fonte
7
@NTDLS A mágica do sistema de classificação realmente funciona pela primeira vez: seis anos depois, e a resposta "mais merecedora" chegou ao topo.
Zxq9
15
As pessoas abaixo continuam dizendo que um bom sistema operacional moderno faz a limpeza, mas e se o código estiver sendo executado no modo kernel (por exemplo, por razões de desempenho)? Os programas no modo kernel (no Linux, por exemplo) estão na caixa de areia? Caso contrário, acredito que você precisaria liberar tudo manualmente, suponho, mesmo antes de quaisquer terminações anormais, como com abort ().
Dr. Pessoa Pessoa II
3
@ Dr.PersonPersonII Sim, o código em execução no modo kernel normalmente precisa liberar tudo manualmente.
Zwol 07/07/19
1
Gostaria de acrescentar que free(a)realmente não faz nada para realmente liberar memória! Ele apenas redefine alguns indicadores na implementação libc do malloc, que monitora os pedaços de memória disponíveis dentro de uma grande página de memória mapeada (normalmente chamada de "heap"). Essa página ainda será liberada somente quando o programa terminar, não antes.
Marco Bonelli
1
Free () pode ou não liberar a memória. Pode apenas marcar o bloco como liberado, para ser recuperado mais tarde, ou pode vinculá-lo a uma lista gratuita. Pode mesclá-lo em blocos livres adjacentes ou deixar para uma alocação subsequente. É tudo um detalhe de implementação.
Jordan Brown

Respostas:

378

Quase todos os sistemas operacionais modernos recuperam todo o espaço de memória alocado após a saída do programa. A única exceção em que posso pensar pode ser algo como o Palm OS, onde o armazenamento estático e a memória de tempo de execução do programa são praticamente a mesma coisa, portanto, não liberar pode fazer com que o programa ocupe mais armazenamento. (Só estou especulando aqui.)

Portanto, geralmente, não há mal nisso, exceto o custo de tempo de execução de ter mais armazenamento do que você precisa. Certamente, no exemplo que você dá, você deseja manter a memória de uma variável que pode ser usada até que seja limpa.

No entanto, é considerado bom estilo liberar memória assim que você não precisar mais dela e liberar qualquer coisa que você ainda tenha na saída do programa. É mais um exercício de saber qual memória você está usando e pensar se você ainda precisa dela. Se você não acompanhar, pode haver vazamento de memória.

Por outro lado, a advertência semelhante para fechar seus arquivos na saída tem um resultado muito mais concreto - se não o fizer, os dados que você escreveu para eles podem não ser liberados ou, se são um arquivo temporário, podem não ser seja excluído quando terminar. Além disso, os identificadores de banco de dados devem ter suas transações confirmadas e fechadas quando você terminar. Da mesma forma, se você estiver usando uma linguagem orientada a objetos como C ++ ou Objective C, não liberar um objeto quando terminar com isso significa que o destruidor nunca será chamado e quaisquer recursos responsáveis ​​pela classe podem não ser limpos.

Paul Tomblin
fonte
16
Provavelmente, também seria bom mencionar que nem todo mundo está usando um sistema operacional moderno, se alguém pega o seu programa (e ele ainda roda em um sistema operacional que não recupera memória) o executa no GG.
user105033
79
Eu realmente considero esta resposta errada. Um deve sempre desalocar recursos depois que um for feito com eles, seja o arquivo manipula / memória / mutexs. Por ter esse hábito, não se cometerá esse tipo de erro ao criar servidores. Alguns servidores devem executar 24x7. Nesses casos, qualquer vazamento de qualquer tipo significa que o servidor acabará ficando sem esse recurso e travará / trava de alguma forma. Um pequeno programa utilitário, um vazamento não é tão ruim assim. Qualquer servidor, qualquer vazamento é a morte. Faça um favor a si mesmo. Limpe depois de si mesmo. É um bom hábito.
31409 EvilTeach
120
Qual parte de "No entanto, é considerado bom estilo liberar memória assim que você não precisar mais dela e liberar qualquer coisa que você ainda tenha na saída do programa". você considera errado, então?
21139 Paul Tomblin
24
Se você possui um armazenamento de memória necessário até o momento em que o programa é encerrado e não está executando um sistema operacional primitivo, liberar a memória imediatamente antes de sair é uma opção estilística, não um defeito.
Paul Tomblin
30
@ Paul - Apenas concordar com o EvilTeach, não é considerado bom estilo para liberar memória, é incorreto não liberar memória. Sua redação faz com que isso pareça tão importante quanto usar um lenço que combine com sua gravata. Na verdade, está no nível de usar calças.
Heath Hunnicutt
110

Sim, você está certo, seu exemplo não faz mal (pelo menos não na maioria dos sistemas operacionais modernos). Toda a memória alocada pelo seu processo será recuperada pelo sistema operacional assim que o processo terminar.

Fonte: Alocação e mitos do GC (alerta PostScript!)

Alocação Mito 4: Os programas sem coleta de lixo devem sempre desalocar toda a memória que alocam.

A verdade: as desalocações omitidas no código frequentemente executado causam vazamentos crescentes. Eles raramente são aceitáveis. mas os programas que retêm a maior parte da memória alocada até a saída do programa geralmente apresentam melhor desempenho sem nenhuma desalocação intermediária. Malloc é muito mais fácil de implementar se não houver livre.

Na maioria dos casos, desalocar memória pouco antes da saída do programa é inútil. O sistema operacional irá recuperá-lo de qualquer maneira. O livre arbítrio toca e pagina nos objetos mortos; o sistema operacional não.

Conseqüência: Tenha cuidado com "detectores de vazamento" que contam alocações. Alguns "vazamentos" são bons!

Dito isto, você realmente deve tentar evitar todos os vazamentos de memória!

Segunda pergunta: seu design está bem. Se você precisar armazenar algo até o aplicativo sair, tudo bem fazer isso com a alocação dinâmica de memória. Se você não souber o tamanho necessário antecipadamente, não poderá usar a memória alocada estaticamente.

compie
fonte
3
Pode ser que a questão, como eu a li, seja o que realmente está acontecendo com a memória vazada, e não se esse exemplo específico está correto. Eu não votaria no contrário, porque ainda é uma boa resposta.
21730 DevinB
3
Provavelmente, existiam (Windows antigo, Mac OS inicial) e talvez ainda existam sistemas operacionais que exijam processos para liberar memória antes da saída, caso contrário, o espaço não será recuperado.
Pete Kirkham
Está perfeitamente bem, a menos que você se preocupe com a fragmentação da memória ou a falta de memória - você faz isso demais e o desempenho de seus aplicativos desaparece. Além dos fatos concretos, siga sempre as melhores práticas e a criação de bons hábitos.
NTDLS 17/03/09
1
Eu tenho uma resposta aceita que está atualmente em torno de -11, então ele nem está concorrendo.
21139 Paul Tomblin
8
Eu acho errado explicar a necessidade de liberar () a memória dizendo "por causa do detector de vazamento". É como dizer "você precisa dirigir devagar em uma rua de jogo, porque os policiais podem estar esperando por você com um radar".
Sebastian Mach
57

=== E quanto a provas futuras e reutilização de código ? ===

Se você não escrever o código para liberar os objetos, estará limitando o código a ser seguro apenas para uso quando puder depender da memória ser liberada pelo processo ser fechado ... ou seja, pequeno uso único projetos ou "descarte" [1] ) ... onde você sabe quando o processo terminará.

Se você faz escrever o código que free () é toda a sua memória alocada dinamicamente, então você está futura prova o código e deixar que os outros usá-lo em um projeto maior.


[1] no que diz respeito a projetos de "descarte". O código usado em projetos "Throw-away" tem uma maneira de não ser jogado fora. Em seguida, você sabe que dez anos se passaram e seu código de "descarte" ainda está sendo usado).

Eu ouvi uma história sobre um cara que escreveu um código apenas por diversão para fazer seu hardware funcionar melhor. Ele disse que " apenas um hobby, não será grande e profissional ". Anos depois, muitas pessoas estão usando seu código de "hobby".

Trevor Boyd Smith
fonte
8
Voto negativo para "pequenos projetos". Existem muitos projetos grandes que intencionalmente não liberam memória na saída porque é uma perda de tempo se você conhece suas plataformas de destino. Na IMO, um exemplo mais preciso teria sido "projetos isolados". Por exemplo, se você estiver criando uma biblioteca reutilizável que será incluída em outros aplicativos, não há um ponto de saída bem definido, para que você não esteja perdendo memória. Para um aplicativo independente, você sempre saberá exatamente quando o processo está terminando e poderá tomar uma decisão consciente de transferir a limpeza para o SO (que deve fazer as verificações de qualquer maneira).
21917 Dan Bechard
O aplicativo de ontem é a função de biblioteca de hoje e amanhã será vinculado a um servidor de longa duração que o chama milhares de vezes.
Adrian McCarthy
1
@AdrianMcCarthy: se uma função verificar se um ponteiro estático é nulo, inicializar com malloc()se for e terminar se o ponteiro ainda for nulo, essa função poderá ser usada com segurança um número arbitrário de vezes, mesmo que freenunca seja chamada. Eu acho que provavelmente vale a pena distinguir vazamentos de memória que podem consumir uma quantidade ilimitada de armazenamento, versus situações que só podem desperdiçar uma quantidade finita e previsível de armazenamento.
Supercat
@ supercat: Meu comentário foi sobre mudanças de código ao longo do tempo. Certamente, vazar uma quantidade limitada de memória não é um problema. Mas um dia alguém vai querer mudar essa função para que não use mais um ponteiro estático. Se o código não tiver condições de liberar a memória apontada, será uma mudança difícil (ou, pior, a mudança será ruim e você terá um vazamento ilimitado).
Adrian McCarthy
1
@AdrianMcCarthy: Alterar o código para não usar mais um ponteiro estático provavelmente exigiria mover o ponteiro para algum tipo de objeto de "contexto" e adicionar código para criar e destruir esses objetos. Se o ponteiro é sempre nullse não houver uma alocação e não é nulo quando existe uma alocação, ter código livre da alocação e definir o ponteiro para nullquando um contexto é destruído seria simples, especialmente se comparado a tudo o que seria necessário fazer para mover objetos estáticos para uma estrutura de contexto.
Supercat
52

Você está correto, nenhum dano é causado e é mais rápido sair

Há várias razões para isto:

  • Todos os ambientes de desktop e servidor simplesmente liberam todo o espaço de memória na saída (). Eles desconhecem estruturas de dados internas do programa, como pilhas.

  • Quase todas as free()implementações nunca retornam memória ao sistema operacional.

  • Mais importante, é um desperdício de tempo quando é feito antes da saída (). Na saída, as páginas de memória e o espaço de troca são simplesmente liberados. Por outro lado, uma série de chamadas free () queima o tempo da CPU e pode resultar em operações de paginação de disco, falhas de cache e despejos de cache.

Com relação à possibilidade de reutilização futura de código, justificando a certeza de operações inúteis: isso é uma consideração, mas não é o caminho ágil . YAGNI!

DigitalRoss
fonte
2
Certa vez, trabalhei em um projeto em que passamos um curto período de tempo tentando entender o uso de memória de um programa (fomos obrigados a dar suporte a ele, não o escrevemos). Com base na experiência, eu concordo anedótico com o seu segundo marcador. No entanto, gostaria de ouvir você (ou alguém) fornecer mais provas de que isso é verdade.
user106740
3
Não importa, encontrou a resposta: stackoverflow.com/questions/1421491/… . Obrigado mesmo!
user106740
@aviggiano que se chama YAGNI.
v.oddou
O princípio YAGNI funciona nos dois sentidos: você nunca precisará otimizar o caminho do desligamento. Otimizações prematuras e tudo isso.
Adrian McCarthy
26

Discordo completamente de todos que dizem que o OP está correto ou que não há danos.

Todo mundo está falando sobre um sistema operacional moderno e / ou legado.

Mas e se eu estiver em um ambiente em que simplesmente não possuo sistema operacional? Onde não há nada?

Imagine agora que você está usando interrupções com estilo de thread e aloque memória. No padrão C ISO / IEC: 9899, ​​a vida útil da memória é declarada como:

7.20.3 Funções de gerenciamento de memória

1 A ordem e a contiguidade do armazenamento alocado por chamadas sucessivas para as funções calloc, malloc e realloc não são especificadas. O ponteiro retornado se a alocação for bem-sucedida é adequadamente alinhado para que possa ser atribuído a um ponteiro para qualquer tipo de objeto e, em seguida, usado para acessar esse objeto ou uma matriz desses objetos no espaço alocado (até que o espaço seja explicitamente desalocado) . O tempo de vida de um objeto alocado se estende da alocação até a desalocação. [...]

Portanto, não é preciso dizer que o ambiente está fazendo o trabalho de liberação para você. Caso contrário, seria adicionado à última frase: "Ou até o programa terminar".

Portanto, em outras palavras: não liberar memória não é apenas uma má prática. Produz código não portátil e não compatível com C. O que pode pelo menos ser visto como 'correto, se o seguinte: [...] for suportado pelo ambiente'.

Mas nos casos em que você não possui sistema operacional, ninguém está fazendo o trabalho por você (eu sei que geralmente você não aloca e realoca a memória em sistemas incorporados, mas há casos em que você pode querer).

Portanto, falando em geral C simples (como o OP está marcado), isso está simplesmente produzindo códigos errados e não portáveis.

dhein
fonte
4
Um contra-argumento é que, se você é um ambiente incorporado, você - como desenvolvedor - seria muito mais exigente em seu gerenciamento de memória. Normalmente, esse é o ponto de realmente pré-alocar a memória fixa estática antecipadamente, em vez de ter mallocs / reallocs de tempo de execução.
John Go-Soco
1
@lunarplasma: Embora o que você está dizendo não esteja incorreto, isso não muda o fato de o que o padrão de idiomas está declarando, e todo mundo que age contra / promove, mesmo que seja pelo senso comum, está produzindo código limitado. Eu posso entender se alguém diz "Eu não preciso me preocupar com isso", pois há casos suficientes em que está tudo bem. MAS que alguém deveria pelo menos saber POR QUE ele não precisa se importar. e especialmente não a omita, desde que uma pergunta não esteja relacionada a esse caso especial. E como o OP está perguntando sobre C em geral sob aspectos teóricos (escolares). Não é bom dizer "Você não precisa"!
Dhein
2
Na maioria dos ambientes em que não há sistema operacional, não há meios pelos quais os programas possam "terminar".
Supercat
@ supercat: Como já escrevi antes: você está certo sobre isso. Mas se alguém está perguntando sobre o assunto em relação às razões do ensino e aos aspectos da escola, não é correto dizer "Você não precisa pensar nisso porque na maioria das vezes isso não importa". A definição é dada por uma razão e, apenas porque a maioria dos ambientes lida com ela, você não pode dizer que não há necessidade de se preocupar. Esse é meu argumento.
Dhein
2
-1 para citar o padrão C, enquanto a maioria NÃO se aplica na ausência de um sistema operacional, pois não há tempo de execução para fornecer os recursos que os mandatos padrão, especialmente no gerenciamento de memória e nas funções da biblioteca padrão (que também estão obviamente ausentes) junto com o tempo de execução / SO).
23

Normalmente libero todos os blocos alocados quando tenho certeza de que estou pronto. Hoje, o ponto de entrada do meu programa pode ser main(int argc, char *argv[]), mas amanhã pode ser foo_entry_point(char **args, struct foo *f)e digitado como um ponteiro de função.

Então, se isso acontecer, agora tenho um vazamento.

Em relação à sua segunda pergunta, se meu programa tivesse uma entrada como a = 5, eu alocaria espaço para a ou realocaria o mesmo espaço em um a = "foo" subsequente. Isso permaneceria alocado até:

  1. O usuário digitou 'desmarcar a'
  2. Minha função de limpeza foi inserida, atendendo a um sinal ou o usuário digitou 'sair'

Não consigo pensar em nenhum sistema operacional moderno que não recupere memória após a saída de um processo. Por outro lado, free () é barato, por que não limpar? Como já foi dito, ferramentas como o valgrind são ótimas para detectar vazamentos com os quais você realmente precisa se preocupar. Mesmo que os blocos que você exemplo sejam rotulados como 'ainda acessíveis', é apenas um ruído extra na saída quando você está tentando garantir que não haja vazamentos.

Outro mito é " Se está em main (), não preciso liberá-lo ", isso está incorreto. Considere o seguinte:

char *t;

for (i=0; i < 255; i++) {
    t = strdup(foo->name);
    let_strtok_eat_away_at(t);
}

Se isso ocorreu antes da bifurcação / daemonização (e, teoricamente, para sempre), seu programa vazou um tamanho indeterminado de t 255 vezes.

Um programa bom e bem escrito deve sempre se limpar. Liberte toda a memória, limpe todos os arquivos, feche todos os descritores, desvincule todos os arquivos temporários, etc. detectar uma falha e retomar.

Realmente, seja gentil com a pobre alma que tem que manter suas coisas quando você passar para outras coisas.

Tim Post
fonte
1
E sim, uma vez um colega de equipe me disse: "Eu nunca preciso ligar para free () em main ()" <shudders>
Tim Post
free() is cheapa menos que você tenha um bilhão de estruturas de dados com relacionamento complexo que precisa liberar uma a uma, percorrer a estrutura de dados para tentar liberar tudo pode acabar aumentando significativamente o tempo de desligamento, especialmente se metade dessa estrutura de dados já estiver paginada para o disco, sem nenhum benefício.
Lie Ryan
3
@LieRyan Se você tem um bilhão, como literalmente em um bilhão de estruturas, certamente tem outros problemas que precisam de um grau de consideração especializado - muito além do escopo desta resposta em particular :)
Tim Post
13

Não há problema em deixar a memória livre quando você sai; malloc () aloca a memória da área de memória chamada "a pilha", e a pilha completa de um processo é liberada quando o processo é encerrado.

Dito isto, uma razão pela qual as pessoas ainda insistem em que é bom liberar tudo antes de sair é que os depuradores de memória (por exemplo, valgrind no Linux) detectam os blocos não liberados como vazamentos de memória e, se você também tiver vazamentos de memória "reais", isso se tornará mais difícil identificá-los se você também obtiver resultados "falsos" no final.

Antti Huima
fonte
1
Valgrind não faz um bom trabalho ao distinguir entre "vazou" e "ainda acessível"?
1755 Christoffer
11
-1 para "completamente bom" É uma prática ruim de codificação deixar a memória alocada sem liberá-la. Se esse código fosse extraído em uma biblioteca, causaria erros de memória em todo o lugar.
21730 DevinB
5
+1 para compensar. Veja a resposta da compie. freeno exitmomento considerado prejudicial.
R .. GitHub Pare de ajudar o gelo
11

Se você estiver usando a memória alocada, não estará fazendo nada de errado. Isso se torna um problema quando você escreve funções (que não sejam principais) que alocam memória sem liberá-la e sem disponibilizá-la para o restante do seu programa. Em seguida, seu programa continua sendo executado com a memória alocada, mas não há como usá-lo. Seu programa e outros programas em execução são privados dessa memória.

Editar: Não é 100% exato dizer que outros programas em execução estão privados dessa memória. O sistema operacional sempre pode permitir que eles o usem à custa da troca do seu programa para a memória virtual ( </handwaving>). O ponto é, no entanto, que se o seu programa libera memória que não está usando, é menos provável que uma troca de memória virtual seja necessária.

Bill the Lizard
fonte
11

Esse código geralmente funciona bem, mas considere o problema de reutilização de código.

Você pode ter escrito algum trecho de código que não libera memória alocada; ele é executado de tal maneira que a memória é recuperada automaticamente. Parece tudo bem.

Depois, outra pessoa copia seu snippet no projeto dele, de forma que ele seja executado mil vezes por segundo. Essa pessoa agora tem um enorme vazamento de memória em seu programa. Não é muito bom em geral, geralmente fatal para um aplicativo de servidor.

A reutilização de código é típica nas empresas. Normalmente, a empresa possui todo o código que seus funcionários produzem e cada departamento pode reutilizar o que a empresa possui. Portanto, ao escrever esse código de "aparência inocente", você pode causar dor de cabeça em potencial a outras pessoas. Isso pode fazer com que você seja demitido.

dente afiado
fonte
2
Pode valer a pena notar a possibilidade não apenas de alguém copiar o trecho, mas também a possibilidade de um programa que foi escrito para executar alguma ação específica depois de ser modificado para fazê-lo repetidamente. Nesse caso, seria bom que a memória fosse alocada uma vez e depois usada repetidamente sem nunca ser liberada, mas alocar e abandonar a memória para cada ação (sem liberá-la) poderia ser desastroso.
Supercat #
7

Qual é o resultado real aqui?

Seu programa vazou a memória. Dependendo do seu sistema operacional, ele pode ter sido recuperado.

Mais moderno de desktop sistemas operacionais fazer recuperar a memória vazada no encerramento do processo, tornando-se, infelizmente, comum a ignorar o problema, como pode ser visto por muitas outras respostas aqui.)

Mas você está confiando em um recurso de segurança que você não deve confiar, e seu programa (ou função) pode ser executado em um sistema onde este comportamento faz resultado em um vazamento de memória "hard", na próxima vez.

Você pode estar executando no modo kernel ou em sistemas operacionais vintage / incorporados que não empregam proteção de memória como compensação. (As MMUs ocupam espaço no disco, a proteção de memória custa ciclos adicionais de CPU e não é pedir muito a um programador para limpar a si próprio).

Você pode usar e reutilizar a memória da maneira que desejar, mas aloque todos os recursos antes de sair.

DevSolar
fonte
5

Na verdade, há uma seção no livro on-line da OSTEP para um curso de graduação em sistemas operacionais que discute exatamente sua pergunta.

A seção relevante é "Esquecendo de liberar memória" no capítulo da API de memória na página 6, que fornece a seguinte explicação:

Em alguns casos, pode parecer que não ligar gratuitamente () é razoável. Por exemplo, seu programa tem vida curta e será encerrado em breve; Nesse caso, quando o processo terminar, o sistema operacional limpará todas as páginas alocadas e, portanto, nenhum vazamento de memória ocorrerá por si só. Embora isso certamente “funcione” (veja o lado na página 7), é provavelmente um mau hábito se desenvolver, portanto, tenha cuidado ao escolher essa estratégia.

Este trecho está no contexto da introdução do conceito de memória virtual. Basicamente, neste ponto do livro, os autores explicam que um dos objetivos de um sistema operacional é "virtualizar memória", ou seja, permitir que todos os programas acreditem que têm acesso a um espaço de endereço de memória muito grande.

Nos bastidores, o sistema operacional traduz "endereços virtuais" que o usuário vê em endereços reais, apontando para a memória física.

No entanto, o compartilhamento de recursos, como memória física, requer que o sistema operacional acompanhe quais processos estão sendo usados. Portanto, se um processo termina, está dentro dos recursos e objetivos de design do sistema operacional recuperar a memória do processo, para que possa redistribuir e compartilhar a memória com outros processos.


EDITAR: O lado mencionado no trecho é copiado abaixo.

Lado: por que nenhuma memória é vazada uma vez que seu processo sai

Ao escrever um programa de curta duração, você pode alocar algum espaço usando malloc(). O programa é executado e está prestes a ser concluído: é necessário chamar (e, portanto, vazar memória na pilha), o sistema operacional recuperará toda a memória do processo (incluindo as páginas de código, pilha e, conforme relevante aqui, heap) quando o programa terminar de executar. Independentemente do estado do seu heap no espaço de endereço, o sistema operacional recupera todas essas páginas quando o processo morre, garantindo assim que nenhuma memória seja perdida, apesar de você não a ter liberado.free() várias vezes antes de sair? Embora pareça errado não, nenhuma memória será "perdida" em nenhum sentido real. O motivo é simples: existem realmente dois níveis de gerenciamento de memória no sistema. O primeiro nível de gerenciamento de memória é executado pelo sistema operacional, que entrega a memória aos processos quando eles são executados e a recupera quando os processos saem (ou morrem). O segundo nível de gerenciamento está em cada processo, por exemplo, no heap quando você chama malloc() efree() . Mesmo se você não ligarfree()

Assim, para programas de curta duração, o vazamento de memória geralmente não causa problemas operacionais (embora possa ser considerado de má forma). Quando você escreve um servidor de longa execução (como um servidor Web ou sistema de gerenciamento de banco de dados, que nunca sai), a memória vazada é um problema muito maior e, eventualmente, causará uma falha quando o aplicativo ficar sem memória. E, é claro, o vazamento de memória é um problema ainda maior dentro de um programa específico: o próprio sistema operacional. Nos mostrando mais uma vez: aqueles que escrevem o código do kernel têm o trabalho mais difícil de todos ...

da página 7 do capítulo API de memória de

Sistemas operacionais: Três peças fáceis
Remzi H. Arpaci-Dusseau e Andrea C. Arpaci-Dusseau Arpaci-Dusseau Books Março de 2015 (Versão 0.90)

spenceryue
fonte
4

Não há perigo real em não liberar suas variáveis, mas se você atribuir um ponteiro a um bloco de memória para um bloco diferente, sem liberar o primeiro bloco, o primeiro bloco não estará mais acessível, mas ocupará espaço. Isso é chamado de vazamento de memória e, se você fizer isso com regularidade, seu processo começará a consumir mais e mais memória, retirando recursos do sistema de outros processos.

Se o processo for de curta duração, você poderá fazê-lo com frequência, pois toda a memória alocada é recuperada pelo sistema operacional quando o processo for concluído, mas eu recomendaria adquirir o hábito de liberar toda a memória para a qual você não tem mais uso.

Kyle Cronin
fonte
1
Quero dizer -1 na sua primeira afirmação "não há perigo", exceto que, em seguida, você fornece uma resposta ponderada sobre por que existe perigo.
21730 DevinB
2
Para os perigos, é bastante benigno - vou levar um vazamento de memória em um segfault a qualquer dia.
22630 Kyle Cronin
1
Muito verdadeiro, e nós dois preferimos nem = D
DevinB
2
@KyleCronin eu muito preferiria ter um segfault que um vazamento de memória, porque ambos são erros graves e segfaults são mais fáceis de detectar. Com muita freqüência, os vazamentos de memória passam despercebidos ou não são resolvidos porque são "bastante benignos". Minha memória RAM e eu sinceramente discordamos.
precisa saber é o seguinte
@ Dan Como desenvolvedor, com certeza. Como usuário, aceitarei o vazamento de memória. Prefiro ter um software que funcione, embora com vazamento de memória, em vez de um software que não funciona.
Kyle Cronin
3

Você está absolutamente correto a esse respeito. Em pequenos programas triviais em que uma variável deve existir até a morte do programa, não há benefício real em desalocar a memória.

De fato, eu já havia participado de um projeto em que cada execução do programa era muito complexa, mas relativamente curta, e a decisão era manter a memória alocada e não desestabilizar o projeto, cometendo erros ao desalocá-lo.

Dito isto, na maioria dos programas isso não é realmente uma opção ou pode levar a falta de memória.

Uri
fonte
2

Você está correto, a memória é automaticamente liberada quando o processo termina. Algumas pessoas se esforçam para não fazer uma limpeza extensa quando o processo é finalizado, pois tudo será devolvido ao sistema operacional. No entanto, enquanto seu programa estiver em execução, você deverá liberar memória não utilizada. Caso contrário, você pode acabar se esgotando ou causar paginação excessiva se o conjunto de trabalho ficar muito grande.

Michael
fonte
2

Se você estiver desenvolvendo um aplicativo a partir do zero, poderá fazer algumas escolhas informadas sobre quando ligar gratuitamente. Seu programa de exemplo é bom: aloca memória, talvez você funcione por alguns segundos e depois fecha, liberando todos os recursos que reivindicou.

No entanto, se você estiver escrevendo algo mais - um servidor / aplicativo de execução demorada ou uma biblioteca para ser usada por outra pessoa, você deve esperar ligar gratuitamente para tudo o que fizer.

Ignorando o lado pragmático por um segundo, é muito mais seguro seguir a abordagem mais rigorosa e forçar-se a libertar tudo o que você faz mal. Se você não tem o hábito de observar vazamentos de memória sempre que codifica, poderá facilmente soltar alguns vazamentos. Então, em outras palavras, sim - você pode fugir sem ele; por favor, tenha cuidado, no entanto.

ojrac
fonte
0

Se um programa esquecer de liberar alguns Megabytes antes de sair, o sistema operacional os libertará. Mas se o seu programa for executado por semanas seguidas e um loop dentro do programa esquecer de liberar alguns bytes em cada iteração, haverá um grande vazamento de memória que consumirá toda a memória disponível no seu computador, a menos que você o reinicie regularmente base => até pequenos vazamentos de memória podem ser ruins se o programa for usado para uma tarefa muito grande, mesmo que originalmente não tenha sido projetado para uma.

Gunter Königsmann
fonte
-2

Penso que seus dois exemplos são, na verdade, apenas um: o free()deve ocorrer apenas no final do processo, o que, como você aponta, é inútil, pois o processo está terminando.

No segundo exemplo, porém, a única diferença é que você permite um número indefinido de malloc(), o que pode levar à falta de memória. A única maneira de lidar com a situação é verificar o código de retorno malloc()e agir em conformidade.

mouviciel
fonte