Quando si esce da un'applicazione C, la memoria mallocata viene liberata automaticamente?


92

Diciamo che ho il seguente codice C:

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

Quando compilo ed eseguo quel programma C, cioè dopo aver allocato un po 'di spazio in memoria, la memoria che ho allocato verrà comunque allocata (cioè occupando sostanzialmente spazio) dopo che esco dall'applicazione e il processo termina?


9
è "buon stile" ripulire la memoria, non perché potresti eseguire su un sistema operativo che non ha memoria protetta (che è il suggerimento principale di seguito), ma perché aumenta la probabilità che tu trovi perdite di memoria e mantiene il tuo codice snello e corretto ...
Matt Joiner

Risposte:


111

Dipende dal sistema operativo. La maggior parte dei sistemi operativi moderni (e tutti i principali) libererà la memoria non liberata dal programma al termine.

Affidarsi a questo è una cattiva pratica ed è meglio liberarlo esplicitamente. Il problema non è solo che il tuo codice sembra brutto. Potresti decidere di integrare il tuo piccolo programma in uno più grande e di lunga durata. Poi, un po 'di tempo dopo, devi passare ore a rintracciare le perdite di memoria.
Affidarsi a una caratteristica di un sistema operativo rende anche il codice meno portabile.


16
Una volta ho incontrato win98 su una piattaforma incorporata e, sulla base di quell'esperienza, posso dire che NON libera memoria quando i programmi si chiudono.
San Jacinto

8
@Ken Era un esempio. Inoltre, c'è una linea tra YAGNI e la codifica sciatta. Non liberare risorse lo attraversa. Il principio YAGNI doveva anche essere applicato alle funzionalità, non al codice che fa funzionare correttamente il programma. (E non liberare memoria è un bug).
Yacoby

5
+1: la cosa più importante da considerare è che la gestione della memoria è come afferma abbastanza correttamente Yacoby: "una caratteristica del sistema operativo" . A meno che non mi sbagli, il linguaggio di programmazione non definisce cosa accade prima o dopo l'esecuzione del programma.
D.Shawley

9
Liberare memoria manualmente richiede più tempo, più codice e introduce la possibilità di bug (dimmi che non hai mai visto un bug nel codice di deallocazione!). Non è "sciatto" omettere intenzionalmente qualcosa che è peggiore in ogni modo per il tuo caso d'uso particolare. A meno che o fino a quando non intendi eseguirlo su un sistema antico / minuscolo che non può liberare pagine dopo la terminazione del processo, o integrarlo in un programma più grande (YAGNI), mi sembra una perdita netta. So che fa male all'ego di un programmatore pensare di non ripulirlo da soli, ma in che modo pratico è effettivamente migliore?
Ken

6
Chiunque proponga perdite di memoria su SO dovrebbe essere privato di ogni reputazione e badge
Ulterior

53

In generale, i moderni sistemi operativi generici eseguono la pulizia dopo che i processi sono terminati . Ciò è necessario perché l'alternativa è che il sistema perda risorse nel tempo e richieda il riavvio a causa di programmi scritti male o semplicemente di bug che si verificano raramente che perdono risorse.

Avere comunque il tuo programma esplicitamente libero dalle sue risorse può essere una buona pratica per vari motivi, come ad esempio:

  • Se si dispone di risorse aggiuntive che non vengono ripulite dal sistema operativo all'uscita, come file temporanei o qualsiasi tipo di modifica allo stato di una risorsa esterna, sarà necessario il codice per gestire tutte queste cose all'uscita e questo è spesso elegantemente combinato con la liberazione della memoria.
  • Se il tuo programma inizia ad avere una vita più lunga, non vorrai che l' unico modo per liberare memoria sia uscire. Ad esempio, potresti voler convertire il tuo programma in un server (daemon) che continua a funzionare mentre gestisce molte richieste per singole unità di lavoro, oppure il tuo programma potrebbe diventare una piccola parte di un programma più grande.

Tuttavia, ecco un motivo per evitare di liberare memoria: arresto efficiente . Ad esempio, supponiamo che la tua applicazione contenga una grande cache in memoria. Se quando esce passa attraverso l'intera struttura della cache e lo libera un pezzo alla volta, ciò non serve a nulla e spreca risorse. In particolare, si consideri il caso in cui le pagine di memoria contenenti la cache siano state scambiate su disco dal sistema operativo; percorrendo la struttura e liberandola stai riportando tutte quelle pagine nella memoria tutte in una volta , sprecando tempo ed energie significative senza alcun beneficio effettivo e possibilmente facendo sì che altri programmi sul sistema vengano scambiati!

Come esempio correlato, ci sono server ad alte prestazioni che funzionano creando un processo per ogni richiesta, quindi facendolo uscire al termine; in questo modo non devono nemmeno tenere traccia dell'allocazione della memoria e non eseguono mai alcuna operazione di liberazione o raccolta di dati inutili, poiché alla fine del processo tutto svanisce nella memoria libera del sistema operativo. (Lo stesso tipo di operazione può essere eseguita all'interno di un processo utilizzando un allocatore di memoria personalizzato, ma richiede una programmazione molto attenta; essenzialmente, creare la propria nozione di "processi leggeri" all'interno del processo del sistema operativo.)


11

Le mie scuse per aver postato così tanto tempo dopo l'ultimo post su questo thread.

Un punto in più. Non tutti i programmi riescono ad uscire graziosi. Arresti anomali e ctrl-C, ecc. Faranno terminare un programma in modi incontrollati. Se il tuo sistema operativo non ha liberato l'heap, ripulito lo stack, eliminato variabili statiche, ecc., Alla fine potresti mandare in crash il tuo sistema a causa di perdite di memoria o peggio.

Interessante a parte questo, gli arresti anomali / interruzioni in Ubuntu e sospetto che tutti gli altri sistemi operativi moderni abbiano problemi con le risorse "gestite". Socket, file, dispositivi, ecc. Possono rimanere "aperti" quando un programma termina / va in crash. è anche buona pratica chiudere qualsiasi cosa con una "maniglia" o "descrittore" come parte della pulizia prima dell'uscita graziosa.

Attualmente sto sviluppando un programma che utilizza pesantemente i socket. Quando rimango bloccato in un blocco devo uscire da esso con Ctrl-C, quindi bloccando le mie prese. Ho aggiunto uno std :: vector per raccogliere un elenco di tutti i socket aperti e un gestore di sigaction che cattura sigint e sigterm. Il gestore scorre l'elenco e chiude i socket. Ho intenzione di fare una routine di pulizia simile da usare prima dei lanci che porteranno alla chiusura prematura.

Qualcuno vuole commentare questo design?


1
Sono contento che tu abbia detto questo, perché avevo un programma che lasciava le risorse del socket in giro e il nostro sistema Ubuntu doveva essere riavviato ogni due settimane o la memoria iniziava a esaurirsi, e c'è molta memoria. Non sono sicuro che le risorse di sistema vengano distrutte se ti dimentichi di ripulirle.
octopusgrabbus

8
Stackoverflow non è un forum; non c'è niente di sbagliato nel rispondere a una vecchia domanda. meta.stackexchange.com/questions/20524/reviving-old-questions
mk12

6

Quello che sta succedendo qui ( in un sistema operativo moderno ) è che il programma viene eseguito all'interno del proprio "processo". Questa è un'entità del sistema operativo dotata del proprio spazio di indirizzi, descrittori di file, ecc. Le mallocchiamate allocano memoria dall '"heap" o pagine di memoria non allocate assegnate al processo.

Quando il tuo programma termina, come in questo esempio, tutte le risorse assegnate al tuo processo vengono semplicemente riciclate / demolite dal sistema operativo. Nel caso della memoria, tutte le pagine di memoria che vi vengono assegnate vengono semplicemente contrassegnate come "libere" e riciclate per l'utilizzo di altri processi. Le pagine sono un concetto di livello inferiore rispetto a quello che gestisce malloc: di conseguenza, le specifiche di malloc / free vengono semplicemente spazzate via mentre l'intera cosa viene ripulita.

È l'equivalente morale di, quando hai finito di usare il tuo laptop e vuoi darlo a un amico, non ti preoccupi di eliminare individualmente ogni file. Devi solo formattare il disco rigido.

Detto questo, come notano tutti gli altri rispondenti, fare affidamento su questa non è una buona pratica:

  1. Dovresti sempre programmare per prenderti cura delle risorse, e in C questo significa anche memoria. Potresti finire per incorporare il tuo codice in una libreria, o potrebbe finire per funzionare molto più a lungo di quanto ti aspetti.
  2. Alcuni sistemi operativi (quelli più vecchi e forse alcuni incorporati moderni) potrebbero non mantenere tali limiti di processo rigidi e le tue allocazioni potrebbero influenzare gli spazi di indirizzi di altri.

4

Sì. Il sistema operativo pulisce le risorse. Beh ... le vecchie versioni di NetWare no.

Modifica: come ha sottolineato San Jacinto, ci sono certamente sistemi (a parte NetWare) che non lo fanno. Anche nei programmi usa e getta, cerco di prendere l'abitudine di liberare tutte le risorse solo per mantenere l'abitudine.


3
Non sto votando negativamente, ma questo è un post piuttosto pericoloso per i posteri. Il DOS è ancora utilizzato su molte piattaforme embedded, e dubito SERIAMENTE che faccia la pulizia della memoria per te. La generalizzazione radicale è sbagliata.
San Jacinto

@San Jacinto: Questo è un buon punto. Questo è il motivo per cui ho fatto il riferimento NetWare, ma probabilmente potrebbe utilizzare un chiarimento. Lo modifico un po '.
Mark Wilkins

3
@San DOS non è un sistema operativo multi-tasking: quando un programma DOS (esclusi i TSR) termina, tutta la memoria è disponibile per il successivo caricamento del programma.

@Neil grazie per il promemoria, ma mi riferivo a un programma simile a TSR che verrebbe avviato quando si verifica un evento, come è un uso comune per i sistemi embedded. Comunque grazie per la tua competenza e chiarimento dove ho fallito :)
San Jacinto

2

Sì, il sistema operativo rilascia tutta la memoria al termine del processo.


Non vedo perché questo è stato downvoted. La memoria di malloc verrà rilasciata quando il processo muore (lo dice la definizione di wikipedia di malloc)
Arve

7
Wikipedia non è il manuale per tutti i sistemi operativi esistenti. La maggior parte dei sistemi operativi moderni recupererà la memoria, ma non tutti (e in particolare non tutti i vecchi) lo fanno. Aggiungete a ciò, mallocposso solo promettere ciò che C farà con la memoria; in base alla progettazione, C non garantisce molto di nulla riguardo al comportamento al di fuori di C stesso. Se l'app muore inaspettatamente, tutte le promesse fatte dalla libreria di runtime sono nulle, poiché non è più viva per mantenerle.
cHao

2

Dipende, i sistemi operativi di solito lo ripuliranno per te, ma se stai lavorando, ad esempio, su un software incorporato, potrebbe non essere rilasciato.

Assicurati solo di liberarlo, può farti risparmiare molto tempo in seguito quando potresti volerlo integrare in un progetto di grandi dimensioni.


0

Dipende davvero dal sistema operativo, ma per tutti i sistemi operativi che incontrerai, l'allocazione della memoria scomparirà quando il processo termina.


0

Penso che la liberazione diretta sia la migliore. Il comportamento indefinito è la cosa peggiore, quindi se hai accesso mentre è ancora definito nel tuo processo, fallo, ci sono molte buone ragioni che le persone hanno dato per questo.

Per quanto riguarda dove, o se, l'ho trovato in W98, la vera domanda era "quando" (non ho visto un post che enfatizza questo). Un piccolo programma modello (per l'input MIDI SysEx, utilizzando vari spazi mallocati) libererebbe memoria nel bit WM_DESTROY di WndProc, ma quando l'ho trapiantato in un programma più grande si è bloccato all'uscita. Ho pensato che questo significasse che stavo cercando di liberare ciò che il sistema operativo aveva già liberato durante una pulizia più ampia. Se l'ho fatto su WM_CLOSE, quindi chiamato DestroyWindow (), ha funzionato tutto bene, uscita pulita istantanea.

Anche se questo non è esattamente lo stesso dei buffer MIDI, c'è una somiglianza in quanto è meglio mantenere intatto il processo, ripulire completamente, quindi uscire. Con modesti blocchi di memoria questo è molto veloce. Ho scoperto che molti piccoli buffer funzionavano più velocemente durante il funzionamento e la pulizia rispetto a pochi buffer grandi.

Possono esistere delle eccezioni, come qualcuno ha detto quando si evita di estrarre grossi blocchi di memoria da un file di scambio su disco, ma anche questo può essere ridotto al minimo mantenendo più e più piccoli spazi allocati.

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.