Cosa succede DAVVERO quando non ti liberi dopo malloc?


538

Questo è stato qualcosa che mi ha infastidito per secoli.

A scuola ci viene insegnato (almeno lo ero) che DEVI liberare ogni puntatore assegnato. Sono un po 'curioso, però, del costo reale di non liberare memoria. In alcuni casi ovvi, come quando mallocviene chiamato all'interno di un ciclo o parte dell'esecuzione di un thread, è molto importante liberarsi, quindi non ci sono perdite di memoria. Ma considera i seguenti due esempi:

Innanzitutto, se ho un codice simile a questo:

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

Qual è il vero risultato qui? Il mio pensiero è che il processo muoia e quindi lo spazio dell'heap è andato comunque, quindi non c'è nulla di male nel perdere la chiamata free(tuttavia, riconosco l'importanza di averlo comunque per la chiusura, la manutenibilità e le buone pratiche). Ho ragione in questo pensiero?

Secondo, diciamo che ho un programma che si comporta un po 'come una shell. Gli utenti possono dichiarare variabili simili aaa = 123e quelle sono archiviate in una struttura di dati dinamica per un uso successivo. Chiaramente, sembra ovvio che useresti una soluzione che chiamerà una funzione * alloc (hashmap, elenco collegato, qualcosa del genere). Per questo tipo di programma, non ha senso liberarsi mai dopo aver chiamato mallocperché queste variabili devono essere sempre presenti durante l'esecuzione del programma e non c'è un buon modo (che posso vedere) per implementarlo con spazio allocato staticamente. È una cattiva progettazione avere un mucchio di memoria allocata ma liberata solo durante la fine del processo? In tal caso, qual è l'alternativa?


7
@NTDLS La magia del sistema di valutazione ha funzionato per una volta: 6 anni dopo e la risposta "più meritevole" è davvero salita al vertice.
zxq9,

15
Le persone sottostanti continuano a dire che un buon sistema operativo moderno esegue la pulizia, ma cosa succede se il codice viene eseguito in modalità kernel (ad esempio, per motivi di prestazioni)? I programmi in modalità kernel (ad esempio in Linux) vengono sottoposti a sandbox? In caso contrario, credo che dovresti liberare tutto manualmente, suppongo, anche prima di eventuali interruzioni anomale come con abort ().
Dr. Person Person II II

3
@ Dr.PersonPersonII Sì, il codice in esecuzione in modalità kernel in genere deve liberare manualmente tutto.
zwol,

1
Vorrei aggiungere che in free(a)realtà non fa nulla per liberare memoria! Ripristina semplicemente alcuni puntatori nell'implementazione libc di malloc che tengono traccia dei blocchi di memoria disponibili all'interno di una grande pagina di memoria mmapped (comunemente chiamata "heap"). La pagina verrà comunque liberata solo al termine del programma, non prima.
Marco Bonelli,

1
Free () potrebbe o meno rilasciare effettivamente la memoria. Potrebbe semplicemente contrassegnare il blocco come liberato, per essere recuperato in seguito o collegarlo a un elenco gratuito. Potrebbe unirlo in blocchi liberi adiacenti o potrebbe lasciarlo per una successiva allocazione. È tutto un dettaglio di implementazione.
Jordan Brown,

Risposte:


378

Quasi tutti i moderni sistemi operativi recupereranno tutto lo spazio di memoria allocato dopo l'uscita da un programma. L'unica eccezione che mi viene in mente potrebbe essere qualcosa come Palm OS in cui l'archiviazione statica del programma e la memoria di runtime sono praticamente la stessa cosa, quindi la mancata liberazione potrebbe far sì che il programma occupi più spazio di archiviazione. (Sto solo speculando qui.)

Quindi, in generale, non vi è alcun danno in esso, tranne il costo di runtime di avere più spazio di archiviazione di cui hai bisogno. Certamente nell'esempio che dai, vuoi conservare la memoria per una variabile che potrebbe essere usata fino a quando non viene cancellata.

Tuttavia, è considerato un buon stile liberare memoria non appena non ne hai più bisogno e liberare tutto ciò che hai ancora all'uscita dal programma. È più un esercizio per sapere quale memoria stai usando e pensare se ne hai ancora bisogno. Se non tieni traccia, potresti avere perdite di memoria.

D'altra parte, la stessa ammonizione di chiudere i tuoi file all'uscita ha un risultato molto più concreto: in caso contrario, i dati che hai scritto potrebbero non essere cancellati, o se sono un file temporaneo, potrebbero non viene eliminato al termine. Inoltre, gli handle del database devono eseguire il commit delle transazioni e quindi chiuderle al termine. Allo stesso modo, se stai usando un linguaggio orientato agli oggetti come C ++ o Objective C, non liberare un oggetto quando hai finito significherà che il distruttore non verrà mai chiamato e qualsiasi risorsa di cui è responsabile la classe potrebbe non essere ripulita.


16
Probabilmente sarebbe anche bene ricordare che non tutti usano un sistema operativo moderno, se qualcuno prende il tuo programma (e funziona ancora su un sistema operativo che non recupera memoria) lo esegue quindi GG.
user105033,

79
Ritengo davvero che questa risposta sia sbagliata. Uno dovrebbe sempre deallocare le risorse dopo averne fatto una, siano esse handle di file / memoria / mutexs. Avendo questa abitudine, non si commetterà questo tipo di errore durante la creazione di server. Alcuni server dovrebbero funzionare 24x7. In questi casi, qualsiasi perdita di qualsiasi tipo significa che il tuo server alla fine esaurirà quella risorsa e si bloccherà / arresterà in qualche modo. Un programma di utilità breve, una perdita non è poi così male. Qualsiasi server, qualsiasi perdita è la morte. Fatti un favore. Pulisci dopo te stesso. È una buona abitudine.
EvilTeach

120
Quale parte di "Tuttavia, è considerato un buon stile per liberare memoria non appena non ne hai più bisogno e per liberare tutto ciò che hai ancora all'uscita dal programma." consideri sbagliato allora?
Paul Tomblin,

24
Se hai un archivio di memoria di cui hai bisogno fino al momento in cui il programma termina e non stai eseguendo un sistema operativo primitivo, liberare la memoria appena prima di uscire è una scelta stilistica, non un difetto.
Paul Tomblin,

30
@Paul - Solo d'accordo con EvilTeach, non è considerato un buon stile per liberare memoria, non è corretto non liberare memoria. La tua formulazione rende questo aspetto importante quanto indossare un fazzoletto che si abbina alla tua cravatta. In realtà, è al livello di indossare i pantaloni.
Heath Hunnicutt,

110

Sì, hai ragione, il tuo esempio non fa alcun danno (almeno non sulla maggior parte dei sistemi operativi moderni). Tutta la memoria allocata dal processo verrà recuperata dal sistema operativo una volta terminato il processo.

Fonte: allocazione e GC Myths (avviso PostScript!)

Allocation Myth 4: I programmi non garbage collection dovrebbero sempre deallocare tutta la memoria che allocano.

La verità: le deallocazioni omesse nel codice eseguito di frequente causano crescenti perdite. Sono raramente accettabili. ma i programmi che conservano la maggior parte della memoria allocata fino all'uscita dal programma spesso funzionano meglio senza intervenire sulla deallocazione. Malloc è molto più facile da implementare se non c'è libero.

Nella maggior parte dei casi, la deallocazione della memoria appena prima dell'uscita dal programma è inutile. Il sistema operativo lo rivendicherà comunque. Tocco libero e sfogliare gli oggetti morti; il sistema operativo non lo farà.

Conseguenza: fare attenzione con i "rilevatori di perdite" che contano le allocazioni. Alcune "perdite" sono buone!

Detto questo, dovresti davvero cercare di evitare tutte le perdite di memoria!

Seconda domanda: il tuo design è ok. Se è necessario archiviare qualcosa fino alla chiusura dell'applicazione, è possibile farlo con allocazione dinamica della memoria. Se non si conosce anticipatamente la dimensione richiesta, non è possibile utilizzare la memoria allocata staticamente.


3
Potrebbe essere perché la domanda, mentre la leggo, è cosa sta realmente accadendo nella memoria trapelata, non se questo esempio specifico sia ok. Non lo voterei però, perché è ancora una buona risposta.
DevinB,

3
Probabilmente c'erano (primi Windows, primi Mac OS), e forse lo sono ancora, sistemi operativi che richiedono processi per liberare memoria prima di uscire, altrimenti lo spazio non viene recuperato.
Pete Kirkham,

Va perfettamente bene, a meno che non ti interessi alla frammentazione della memoria o all'esaurimento della memoria: lo fai troppo e le prestazioni delle tue applicazioni scompariranno. Oltre ai fatti concreti, segui sempre le migliori pratiche e la costruzione di buone abitudini.
NTDLS

1
Ho una risposta accettata che si trova attualmente attorno a -11, quindi non è nemmeno in corsa per la cronaca.
Paul Tomblin,

8
Penso che sia sbagliato spiegare la necessità di liberare la memoria dicendo "a causa del rilevatore di perdite". È come dire "devi guidare lentamente in una strada da gioco perché i poliziotti potrebbero aspettarti con un autovelox".
Sebastian Mach,

57

=== Che dire di future prove e riutilizzo del codice ? ===

Se non scrivi il codice per liberare gli oggetti, stai limitando il codice ad essere sicuro da usare solo quando puoi dipendere dalla memoria libera dal processo che si sta chiudendo ... cioè un piccolo uso una tantum progetti o "usa e getta" [1] ) ... dove sai quando finirà il processo.

Se fate scrivere il codice che il libero () s tutta la memoria allocata dinamicamente, allora siete prova di futuro il codice e lasciare che gli altri lo usano in un progetto più ampio.


[1] per quanto riguarda i progetti "usa e getta". Il codice utilizzato nei progetti "butta via" ha un modo di non essere gettato via. La prossima cosa che sai sono passati dieci anni e il tuo codice "usa e getta" è ancora in uso).

Ho sentito la storia di un ragazzo che ha scritto del codice solo per divertimento per far funzionare meglio il suo hardware. Ha detto " solo un hobby, non sarà grande e professionale ". Anni dopo molte persone usano il suo codice "hobby".


8
Downvoted per "piccoli progetti". Esistono molti progetti di grandi dimensioni che intenzionalmente non liberano memoria all'uscita perché è una perdita di tempo se si conoscono le piattaforme di destinazione. IMO, un esempio più accurato sarebbero stati "progetti isolati". Ad esempio, se stai creando una libreria riutilizzabile che verrà inclusa in altre applicazioni, non esiste un punto di uscita ben definito, quindi non dovresti perdere la memoria. Per un'applicazione autonoma, saprai sempre esattamente quando il processo è terminato e puoi prendere una decisione consapevole di scaricare la pulizia sul sistema operativo (che deve fare i controlli in entrambi i modi).
Dan Bechard,

L'applicazione di ieri è la funzione di libreria di oggi e domani sarà collegata a un server di lunga durata che la chiama migliaia di volte.
Adrian McCarthy,

1
@AdrianMcCarthy: se una funzione verifica se un puntatore statico è nullo, lo inizializza con malloc()se lo è e termina se il puntatore è ancora nullo, tale funzione può essere tranquillamente utilizzata un numero arbitrario di volte anche se freenon viene mai chiamata. Penso che probabilmente valga la pena distinguere le perdite di memoria che possono utilizzare una quantità illimitata di spazio di archiviazione, rispetto alle situazioni che possono solo sprecare una quantità limitata e prevedibile di spazio di archiviazione.
supercat

@supercat: il mio commento parlava della modifica del codice nel tempo. Certo, perdere una quantità limitata di memoria non è un problema. Ma un giorno qualcuno vorrà cambiare quella funzione in modo che non stia più usando un puntatore statico. Se il codice non prevede la possibilità di rilasciare la memoria puntata, si tratterà di una modifica difficile (o, peggio ancora, la modifica sarà errata e si otterrà una perdita illimitata).
Adrian McCarthy,

1
@AdrianMcCarthy: la modifica del codice per non utilizzare più un puntatore statico richiederebbe probabilmente lo spostamento del puntatore in una sorta di oggetto "contestuale" e l'aggiunta di codice per creare e distruggere tali oggetti. Se il puntatore è sempre nullse non esiste alcuna allocazione, e non nullo quando esiste un'allocazione, avere il codice libera l'allocazione e impostare il puntatore su nullquando un contesto viene distrutto sarebbe semplice, soprattutto rispetto a tutto il resto che dovrebbe essere fatto per spostare oggetti statici in una struttura di contesto.
supercat

52

Hai ragione, non viene fatto alcun danno ed è più veloce uscire

Ci sono diverse ragioni per questo:

  • Tutti gli ambienti desktop e server rilasciano semplicemente l'intero spazio di memoria su exit (). Non sono a conoscenza delle strutture di dati interne al programma come gli heap.

  • Quasi tutte le free()implementazioni non restituiscono comunque la memoria al sistema operativo.

  • Ancora più importante, è una perdita di tempo se fatto subito prima di exit (). All'uscita, le pagine di memoria e lo spazio di swap vengono semplicemente rilasciati. Al contrario, una serie di chiamate free () bruciano il tempo della CPU e possono causare operazioni di paging del disco, errori nella cache e sfratti della cache.

Per quanto riguarda la possibilità di un futuro riutilizzo del codice che giustifica la certezza di operazioni inutili: questa è una considerazione, ma probabilmente non è il modo Agile . YAGNI!


2
Una volta ho lavorato a un progetto in cui abbiamo trascorso un breve periodo di tempo cercando di capire l'utilizzo della memoria di un programma (ci era richiesto di supportarlo, non lo abbiamo scritto). Sulla base dell'esperienza concordo aneddoticamente con il tuo secondo proiettile. Tuttavia, mi piacerebbe sentire te (o qualcuno) fornire ulteriori prove che ciò sia vero.
user106740

3
Non importa, ho trovato la risposta: stackoverflow.com/questions/1421491/… . Grazie mille!
user106740

@aviggiano che si chiama YAGNI.
v. L'

Il principio YAGNI funziona in entrambi i modi: non dovrai mai ottimizzare il percorso di spegnimento. Ottimizzazioni premature e tutto il resto.
Adrian McCarthy,

26

Sono completamente in disaccordo con tutti coloro che affermano che OP è corretto o che non vi è alcun danno.

Tutti parlano di un sistema operativo moderno e / o legacy.

Ma cosa succede se mi trovo in un ambiente in cui semplicemente non ho un sistema operativo? Dove non c'è niente?

Immagina ora di utilizzare interrupt in stile thread e di allocare memoria. Nella norma C ISO / IEC: 9899 è la durata della memoria dichiarata come:

7.20.3 Funzioni di gestione della memoria

1 L'ordine e la contiguità della memoria allocata dalle chiamate successive alle funzioni calloc, malloc e realloc non sono specificate. Il puntatore restituito se l'allocazione ha esito positivo è opportunamente allineato in modo che possa essere assegnato a un puntatore a qualsiasi tipo di oggetto e quindi utilizzato per accedere a tale oggetto o ad una matrice di tali oggetti nello spazio allocato (fino a quando lo spazio non viene esplicitamente deallocato) . La durata di un oggetto allocato si estende dall'allocazione fino alla deallocazione. [...]

Quindi non si deve dare per scontato che l'ambiente stia facendo il lavoro di liberazione per te. Altrimenti verrebbe aggiunto all'ultima frase: "O fino al termine del programma."

Quindi, in altre parole: non liberare memoria non è solo una cattiva pratica. Produce codice non portatile e non conforme a C. Che può almeno essere visto come "corretto, se quanto segue: [...], è supportato dall'ambiente".

Ma nei casi in cui non hai affatto un sistema operativo, nessuno sta facendo il lavoro per te (so che generalmente non allochi e riallochi la memoria su sistemi incorporati, ma ci sono casi in cui potresti volerlo.)

Quindi parlando in generale C (come quello che l'OP è etichettato), questo sta semplicemente producendo un codice errato e non portatile.


4
Un contro-argomento è che se sei un ambiente incorporato, tu, come sviluppatore, saresti molto più esigente nella gestione della memoria in primo luogo. Di solito, questo è al punto di pre-allocare in anticipo la memoria fissa statica piuttosto che avere qualsiasi runtime malloc / realloc.
John Go-Soco,

1
@lunarplasma: Anche se ciò che stai dicendo non è errato, ciò non cambia il fatto che lo standard linguistico sta affermando, e tutti coloro che agiscono contro di esso, può anche essere di buon senso, stanno producendo un codice limitato. Posso capire se qualcuno dice "Non devo preoccuparmene", poiché ci sono abbastanza casi in cui va bene. MA almeno uno dovrebbe sapere PERCHÉ non deve preoccuparsene. e soprattutto non ometterlo finché una domanda non è correlata a quel caso speciale. E dal momento che OP sta chiedendo di C in generale sotto aspetti teorici (scolastici). Non va bene dire "Non è necessario"!
Dhein,

2
Nella maggior parte degli ambienti in cui non esiste un sistema operativo, non esiste alcun mezzo attraverso il quale i programmi possano "terminare".
supercat

@supercat: Come ho scritto prima: hai ragione. Ma se qualcuno lo sta chiedendo riguardo ai motivi dell'insegnamento e agli aspetti scolastici, non è giusto dire "Non devi pensarci sicne il più delle volte non importa" La formulazione e il comportamento della lingua la definizione è data per una ragione, e solo perché la maggior parte degli ambienti la gestisce per te, non puoi dire che non c'è bisogno di preoccuparsene. Questo è il mio punto.
Dhein,

2
-1 per la citazione dello standard C mentre la maggior parte NON si applica in assenza di un sistema operativo, in quanto non esiste alcun tempo di esecuzione per fornire le funzionalità dei mandati standard, in particolare per quanto riguarda la gestione della memoria e le funzioni della libreria standard (che sono ovviamente assenti insieme al runtime / OS).

23

In genere, libero ogni blocco allocato una volta che sono sicuro di averlo fatto. Oggi, il punto di ingresso del mio programma potrebbe essere main(int argc, char *argv[]), ma domani potrebbe essere foo_entry_point(char **args, struct foo *f)e digitato come puntatore a funzione.

Quindi, se ciò accade, ora ho una perdita.

Per quanto riguarda la tua seconda domanda, se il mio programma prendesse input come a = 5, assegnerei lo spazio per a, o riassegnerei lo stesso spazio su un successivo "= pippo". Ciò rimarrebbe assegnato fino a quando:

  1. L'utente ha digitato 'unset a'
  2. La mia funzione di pulizia è stata inserita, eseguendo la manutenzione di un segnale o digitando "esci" dall'utente

Non riesco a pensare a nessun sistema operativo moderno che non recupera memoria dopo la chiusura di un processo. Poi di nuovo, free () è economico, perché non ripulire? Come altri hanno già detto, strumenti come valgrind sono ottimi per individuare le perdite di cui devi davvero preoccuparti. Anche se i blocchi che tu esempio verrebbero etichettati come 'ancora raggiungibili', è solo un rumore extra nell'output quando stai cercando di assicurarti di non avere perdite.

Un altro mito è " Se è in main (), non devo liberarlo ", questo non è corretto. Considera quanto segue:

char *t;

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

Se ciò è avvenuto prima del biforcazione / demonizzazione (e in teoria in esecuzione per sempre), il tuo programma ha appena trapelato una dimensione indeterminata di t 255 volte.

Un buon programma ben scritto dovrebbe sempre ripulire dopo se stesso. Liberare tutta la memoria, svuotare tutti i file, chiudere tutti i descrittori, scollegare tutti i file temporanei, ecc. Questa funzione di pulizia dovrebbe essere raggiunta al termine normale o alla ricezione di vari tipi di segnali fatali, a meno che non si desideri lasciare alcuni file in posa in modo da poter rilevare un arresto anomalo e riprendere.

Davvero, sii gentile con la povera anima che deve mantenere la tua roba quando passi ad altre cose .. consegnala a loro 'valgrind clean' :)


1
E sì, una volta ho avuto un compagno di squadra che mi diceva: "Non ho mai bisogno di chiamare free () in main ()" <shudders>
Tim Post

free() is cheapa meno che tu non abbia un miliardo di strutture di dati con relazioni complesse che devi rilasciare una per una, attraversare la struttura di dati per provare a rilasciare tutto potrebbe finire per aumentare significativamente i tempi di spegnimento, specialmente se metà di quella struttura di dati è già pagata sul disco, senza alcun vantaggio.
Lie Ryan

3
@LieRyan Se hai un miliardo, come in letteralmente un miliardo di strutture, sicuramente hai altri problemi che richiedono un grado di considerazione specializzato - ben oltre lo scopo di questa particolare risposta :)
Tim Post

13

Va completamente bene lasciare la memoria libera quando si esce; malloc () alloca la memoria dall'area di memoria chiamata "heap" e l'heap completo di un processo viene liberato quando il processo termina.

Detto questo, uno dei motivi per cui le persone insistono ancora sul fatto che è bene liberare tutto prima di uscire è che i debugger di memoria (ad esempio valgrind su Linux) rilevano i blocchi non ordinati come perdite di memoria, e se hai anche perdite di memoria "reali", diventa più difficile individuarli se si ottengono anche risultati "falsi" alla fine.


1
Valgrind non fa un buon lavoro distinguendo tra "trapelato" e "ancora raggiungibile"?
Christoffer

11
-1 per "completamente bene" È una buona pratica di codifica lasciare la memoria allocata senza liberarla. Se quel codice fosse estratto in una libreria, causerebbe memleak dappertutto.
DevinB,

5
+1 per compensare. Vedi la risposta di compie. freeal exitmomento considerato dannoso.
R .. GitHub smette di aiutare ICE il

11

Se stai usando la memoria che hai allocato, non stai facendo nulla di male. Diventa un problema quando si scrivono funzioni (diverse da quelle principali) che allocano la memoria senza liberarla e senza renderla disponibile al resto del programma. Quindi il programma continua a funzionare con quella memoria allocata, ma non è possibile utilizzarlo. Il tuo programma e altri programmi in esecuzione sono privati ​​di quella memoria.

Modifica: non è preciso al 100% affermare che altri programmi in esecuzione siano privati ​​di quella memoria. Il sistema operativo può sempre lasciarlo utilizzare a spese dello scambio del programma nella memoria virtuale ( </handwaving>). Il punto è, tuttavia, che se il programma libera la memoria che non utilizza, è meno probabile che uno scambio di memoria virtuale sia necessario.


11

Questo codice di solito funziona bene, ma considera il problema del riutilizzo del codice.

È possibile che tu abbia scritto un frammento di codice che non libera la memoria allocata, ma viene eseguito in modo tale da recuperare automaticamente la memoria. Sembra tutto a posto.

Quindi qualcun altro copia il tuo frammento nel suo progetto in modo che venga eseguito mille volte al secondo. Quella persona ora ha un'enorme perdita di memoria nel suo programma. Non molto buono in generale, di solito fatale per un'applicazione server.

Il riutilizzo del codice è tipico nelle aziende. Di solito la società possiede tutto il codice che i suoi dipendenti producono e ogni dipartimento può riutilizzare qualunque cosa la società possieda. Quindi, scrivendo un codice "dall'aspetto innocente", potresti causare mal di testa ad altre persone. Questo potrebbe farti licenziare.


2
Potrebbe valere la pena notare la possibilità non solo di qualcuno di copiare lo snippet, ma anche la possibilità di un programma che è stato scritto per eseguire una determinata azione una volta modificato per farlo ripetutamente. In tal caso, sarebbe bene che la memoria fosse allocata una volta e poi usata ripetutamente senza mai essere liberata, ma allocare e abbandonare la memoria per ogni azione (senza liberarla) potrebbe essere disastrosa.
supercat

7

Qual è il vero risultato qui?

Il tuo programma ha fatto trapelare la memoria. A seconda del sistema operativo in uso, potrebbe essere stato ripristinato.

La maggior parte dei moderni sistemi operativi desktop lo fanno recuperare memoria persa a terminazione dei processi, il che rende tristemente comune a ignorare il problema, come si può vedere da molte altre risposte qui.)

Ma stai facendo affidamento su una funzionalità di sicurezza su cui non dovresti fare affidamento e il tuo programma (o funzione) potrebbe essere eseguito su un sistema in cui questo comportamento provoca una perdita di memoria "difficile", la prossima volta.

Potrebbe essere in esecuzione in modalità kernel o su sistemi operativi vintage / incorporati che non utilizzano la protezione della memoria come compromesso. (Le MMU occupano spazio, la protezione della memoria costa ulteriori cicli della CPU e non è troppo chiedere a un programmatore di ripulire dopo se stesso).

Puoi usare e riutilizzare la memoria come preferisci, ma assicurati di aver deallocato tutte le risorse prima di uscire.


5

In realtà c'è una sezione nel libro di testo online OSTEP per un corso di laurea in sistemi operativi che discute esattamente la tua domanda.

La sezione pertinente è "Dimenticare la memoria libera" nel capitolo API di memoria a pagina 6 che fornisce la seguente spiegazione:

In alcuni casi, può sembrare ragionevole non chiamare free (). Ad esempio, il tuo programma è di breve durata e presto uscirà; in questo caso, quando il processo termina, il sistema operativo pulirà tutte le pagine allocate e quindi non si verificherà alcuna perdita di memoria. Mentre questo sicuramente "funziona" (vedi il lato a pagina 7), è probabilmente una cattiva abitudine da sviluppare, quindi diffidare di scegliere una tale strategia

Questo estratto è nel contesto dell'introduzione del concetto di memoria virtuale. Fondamentalmente a questo punto del libro, gli autori spiegano che uno degli obiettivi di un sistema operativo è "virtualizzare la memoria", ovvero lasciare che ogni programma creda di avere accesso a uno spazio di indirizzi di memoria molto ampio.

Dietro le quinte, il sistema operativo tradurrà "indirizzi virtuali" che l'utente vede in indirizzi reali che puntano alla memoria fisica.

Tuttavia, la condivisione di risorse come la memoria fisica richiede al sistema operativo di tenere traccia di quali processi la stanno utilizzando. Quindi, se un processo termina, rientra nelle capacità e negli obiettivi di progettazione del sistema operativo recuperare la memoria del processo in modo che possa ridistribuire e condividere la memoria con altri processi.


EDIT: il lato menzionato nell'estratto è copiato di seguito.

A PARTIRE: PERCHÉ NESSUNA MEMORIA È LASCIATA UNA VOLTA DAL VOSTRO PROCESSO

Quando si scrive un programma di breve durata, è possibile allocare spazio utilizzando malloc(). Il programma funziona e sta per essere completato: è necessario chiamare free()un sacco di volte prima di uscire? Anche se sembra sbagliato non farlo, nessun ricordo sarà "perso" in alcun senso reale. Il motivo è semplice: ci sono davvero due livelli di gestione della memoria nel sistema. Il primo livello di gestione della memoria viene eseguito dal sistema operativo, che distribuisce la memoria ai processi quando vengono eseguiti e la riprende quando i processi terminano (o altrimenti muoiono). Il secondo livello di gestione è all'interno di ciascun processo, ad esempio all'interno dell'heap quando si chiama malloc()efree() . Anche se non riesci a chiamarefree()(e quindi perdita di memoria nell'heap), il sistema operativo recupererà tutta la memoria del processo (comprese quelle pagine per codice, stack e, come pertinente qui, heap) al termine dell'esecuzione del programma. Indipendentemente dallo stato del tuo heap nel tuo spazio degli indirizzi, il sistema operativo riprende tutte quelle pagine quando il processo termina, assicurando così che nessuna memoria vada persa nonostante il fatto che non l'hai liberata.

Pertanto, per i programmi di breve durata, la perdita di memoria spesso non causa problemi operativi (sebbene possa essere considerata una forma scadente). Quando si scrive un server a esecuzione prolungata (come un server Web o un sistema di gestione del database, che non si chiude mai), la perdita di memoria è un problema molto più grande e alla fine porta a un arresto anomalo quando l'applicazione esaurisce la memoria. E, naturalmente, la perdita di memoria è un problema ancora più grande all'interno di un particolare programma: il sistema operativo stesso. Ci mostra ancora una volta: coloro che scrivono il codice del kernel hanno il lavoro più duro di tutti ...

dalla pagina 7 del capitolo API di memoria di

Sistemi operativi: tre pezzi facili
Remzi H. Arpaci-Dusseau e Andrea C. Arpaci-Dusseau Libri Arpaci-Dusseau Marzo 2015 (Versione 0.90)


4

Non c'è nessun vero pericolo nel non liberare le variabili, ma se si assegna un puntatore a un blocco di memoria a un diverso blocco di memoria senza liberare il primo blocco, il primo blocco non è più accessibile ma occupa ancora spazio. Questo è ciò che viene chiamato perdita di memoria e, se lo fai con regolarità, il tuo processo inizierà a consumare sempre più memoria, togliendo risorse di sistema da altri processi.

Se il processo è di breve durata, è spesso possibile evitarlo, poiché al termine del processo tutta la memoria allocata viene recuperata dal sistema operativo, ma consiglio di prendere l'abitudine di liberare tutta la memoria per la quale non è più necessario.


1
Voglio dire -1 per la tua prima affermazione "non c'è pericolo" tranne per il fatto che poi dai una risposta ponderata sul perché c'è il pericolo.
DevinB,

2
Dato che i pericoli vanno, è piuttosto benigno - ogni giorno colgo una perdita di memoria su un segfault.
Kyle Cronin,


2
@KyleCronin Preferirei di gran lunga avere un segfault piuttosto che una perdita di memoria, perché entrambi sono bug gravi e i segfault sono più facili da rilevare. Troppo spesso le perdite di memoria passano inosservate o irrisolte perché sono "piuttosto benigne". Io e la mia RAM non siamo assolutamente d'accordo.
Dan Bechard,

@Dan Come sviluppatore, certo. Come utente, prenderò la perdita di memoria. Preferirei avere software che funzioni, anche se con perdita di memoria, rispetto a software che non lo fa.
Kyle Cronin,

3

Hai assolutamente ragione sotto questo aspetto. Nei piccoli programmi banali in cui una variabile deve esistere fino alla morte del programma, non vi è alcun reale vantaggio nel deallocare la memoria.

In effetti, una volta ero stato coinvolto in un progetto in cui ogni esecuzione del programma era molto complessa ma relativamente di breve durata, e la decisione era quella di mantenere la memoria allocata e non destabilizzare il progetto commettendo errori nel collocarlo.

Detto questo, nella maggior parte dei programmi questa non è davvero un'opzione, o può portare a rimanere senza memoria.


2

Hai ragione, la memoria viene automaticamente liberata quando termina il processo. Alcune persone si sforzano di non eseguire una pulizia approfondita al termine del processo, poiché verrà abbandonato al sistema operativo. Tuttavia, mentre il programma è in esecuzione, è necessario liberare memoria inutilizzata. In caso contrario, alla fine potresti esaurire o causare un paging eccessivo se il tuo set di lavoro diventa troppo grande.


2

Se stai sviluppando un'applicazione da zero, puoi fare alcune scelte istruite su quando chiamare gratuitamente. Il tuo programma di esempio va bene: alloca memoria, forse hai un po 'di lavoro per qualche secondo, quindi si chiude, liberando tutte le risorse che ha rivendicato.

Se stai scrivendo qualcos'altro, però: un server / un'applicazione a esecuzione prolungata o una libreria che verrà utilizzata da qualcun altro, dovresti aspettarti di chiamare gratuitamente su tutto ciò che malloc.

Ignorando il lato pragmatico per un secondo, è molto più sicuro seguire un approccio più rigoroso e forzarti a liberare tutto ciò che mali. Se non hai l'abitudine di cercare perdite di memoria ogni volta che scrivi un codice, potresti facilmente generare qualche perdita. Quindi, in altre parole, sì: puoi scappare senza di essa; per favore stai attento, però.


0

Se un programma dimentica di liberare qualche Megabyte prima di uscire, il sistema operativo li libererà. Ma se il tuo programma viene eseguito per settimane alla volta e un ciclo all'interno del programma dimentica di liberare pochi byte in ogni iterazione, avrai una potente perdita di memoria che consumerà tutta la memoria disponibile nel tuo computer a meno che non lo riavvii regolarmente base => anche piccole perdite di memoria potrebbero essere dannose se il programma viene utilizzato per un'attività seriamente grande anche se in origine non era progettato per uno.


-2

Penso che i tuoi due esempi siano in realtà solo uno: free()dovrebbero accadere solo alla fine del processo, che come fai notare è inutile poiché il processo sta terminando.

Nel secondo esempio, tuttavia, l'unica differenza è che si consente un numero indefinito di malloc(), che potrebbe portare all'esaurimento della memoria. L'unico modo per gestire la situazione è controllare il codice di ritorno malloc()e agire di conseguenza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.