Fino a che punto possono arrivare le perdite di memoria?


118

Mi sono imbattuto in perdite di memoria molte volte. Di solito quando sto ... malloccome se non ci fosse un domani, o penzolavo FILE *come se fosse un bucato sporco. In genere presumo (leggi: spero disperatamente) che tutta la memoria venga ripulita almeno quando il programma termina. Ci sono situazioni in cui la memoria persa non verrà raccolta quando il programma termina o si blocca?

Se la risposta varia notevolmente da lingua a lingua, concentriamoci su C (++).

Nota l'uso iperbolico della frase "come se non ci fosse un domani" e "penzoloni ... come panni sporchi". Non sicuro * malloc* può ferire le persone che ami. Inoltre, prestare attenzione con la biancheria sporca.


3
Se stai utilizzando un sistema operativo "moderno" come Linux o Windows, il sistema operativo stesso risolverà la memoria non rilasciata quando il programma termina.
Oliver Charlesworth

60
Invece di malloppare come se non ci fosse un domani, prova a fingere che ci sia un domani e tieni traccia della tua memoria!
William Pursell

8
@WilliamPursell ah, quindi stai dicendo che uno dovrebbe calloccome se non ci fosse un domani. Eccellente.
DilithiumMatrix

8
"Se la risposta varia notevolmente da lingua a lingua, concentriamoci su c (++)". c e c ++ non sono la stessa lingua!
Johnsyweb

11
@zhermes: Il commento sul fatto che C e C ++ sono linguaggi diversi nasconde più di quanto pensi ... In C ++ ti ritroverai piuttosto a sfruttare gli oggetti con durata di memorizzazione automatica, segui il linguaggio RAII ... lascia che questi oggetti si occupino della memoria gestione per te.
LihO

Risposte:


111

No. I sistemi operativi liberano tutte le risorse detenute dai processi quando escono.

Questo vale per tutte le risorse mantenute dal sistema operativo: memoria, file aperti, connessioni di rete, maniglie delle finestre ...

Detto questo, se il programma è in esecuzione su un sistema integrato senza un sistema operativo, o con un sistema operativo molto semplice o difettoso, la memoria potrebbe essere inutilizzabile fino al riavvio. Ma se tu fossi in quella situazione probabilmente non staresti facendo questa domanda.

Il sistema operativo potrebbe impiegare molto tempo per liberare alcune risorse. Ad esempio, la porta TCP che un server di rete utilizza per accettare le connessioni potrebbe impiegare alcuni minuti per liberarsi, anche se correttamente chiusa dal programma. Un programma in rete può anche contenere risorse remote come oggetti di database. Il sistema remoto dovrebbe liberare queste risorse quando si perde la connessione di rete, ma potrebbe essere necessario anche più tempo del sistema operativo locale.


5
Un paradigma comune negli RTOS è il modello a processo singolo, a più thread e nessuna protezione della memoria tra le "attività". Di solito c'è un mucchio. Questo è certamente il modo in cui VxWorks funzionava e probabilmente lo fa ancora.
marko

29
Notare che non tutte le risorse possono essere liberate dal sistema operativo. Connessioni di rete, transazioni di database e così via, la mancata chiusura esplicita può causare risultati indesiderati. La mancata chiusura della connessione di rete potrebbe far pensare al server che sei ancora attivo per un periodo di tempo indefinito e, per i server che limitano il numero di connessioni attive, potrebbe causare accidentalmente la negazione del servizio. La mancata chiusura delle transazioni del database potrebbe causare la perdita di dati non salvati.
Lie Ryan

1
@Marko: la versione recente di vxWorks ora supporta RTP (processi in tempo reale) che supportano la protezione della memoria.
Xavier T.

20
"I sistemi operativi liberano tutte le risorse detenute dai processi quando escono." Non del tutto vero. Ad esempio, su (almeno) Linux, i semafori SysV e altri oggetti IPC non vengono ripuliti all'uscita dal processo. Ecco perché c'è ipcrmper la pulizia manuale, linux.die.net/man/8/ipcrm .
sleske

7
Inoltre, se un oggetto ha un file temporaneo che mantiene, chiaramente non verrà cancellato in seguito.
Mooing Duck

47

Lo standard C non specifica che la memoria allocata da mallocviene rilasciata quando il programma termina. Ciò viene fatto dal sistema operativo e non tutti i sistemi operativi (di solito questi sono nel mondo embedded) rilasciano la memoria quando il programma termina.


20
Questo è più o meno perché lo standard C parla di programmi C, non dei sistemi operativi su cui C è in esecuzione ...
vonbrand

5
@vonbrand Lo standard C avrebbe potuto avere un paragrafo che dice che quando mainritorna tutta la memoria allocata da mallocviene rilasciata. Ad esempio, dice che tutti i file aperti vengono chiusi prima della fine del programma. Per la memoria allocata malloc, semplicemente non è specificato. Ora, ovviamente, la mia frase riguardante il sistema operativo descrive ciò che di solito viene fatto e non ciò che lo Standard prescrive, poiché non specifica nulla su questo.
ouah

Vorrei correggere il mio commento: lo standard parla di C, non di come il programma viene avviato e interrotto. Puoi benissimo scrivere un programma C che gira senza un sistema operativo. In quel caso non c'è nessuno che farà la pulizia. Lo standard molto volutamente non specifica nulla a meno necessario, in modo da usi non vincolare senza necessità.
vonbrand

2
@ouah: " quando torna principale ...". È un'ipotesi. Dobbiamo considerare " se i rendimenti principali ...". std::atexitconsidera anche la chiusura del programma tramite std::exit, e poi c'è anche std::aborte (specifico per C ++) std::terminate.
MSalters

@ouah: Se fosse stato incluso, atexitnon sarebbe utilizzabile. :-)
R .. GitHub SMETTA DI AIUTARE IL GHIACCIO

28

Poiché tutte le risposte hanno coperto la maggior parte degli aspetti della tua domanda rispetto ai sistemi operativi moderni, ma storicamente, ce n'è una che vale la pena menzionare se hai mai programmato nel mondo DOS. I programmi Terminant e Stay Resident (TSR) restituiscono solitamente il controllo al sistema, ma risiedono in una memoria che potrebbe essere ripristinata da un interrupt software / hardware. Era normale vedere messaggi come "memoria esaurita! Prova a scaricare alcuni dei tuoi TSR" quando lavori su questi sistemi operativi.

Quindi tecnicamente il programma termina , ma poiché risiede ancora nella memoria, qualsiasi perdita di memoria non verrà rilasciata a meno che non si scarichi il programma.

Quindi puoi considerare questo un altro caso a parte il fatto che i sistemi operativi non recuperano memoria o perché è difettoso o perché il sistema operativo incorporato è progettato per farlo.

Ricordo un altro esempio. Il Customer Information Control System (CICS), un server di transazione che viene eseguito principalmente su mainframe IBM, è pseudo-conversazionale. Quando viene eseguito, elabora i dati inseriti dall'utente, genera un altro set di dati per l'utente, trasferisce al nodo terminale dell'utente e termina. All'attivazione del tasto di attenzione, si rianima nuovamente per elaborare un altro set di dati. Poiché il modo in cui si comporta, ancora tecnicamente, il sistema operativo non recupererà la memoria dai programmi CICS terminati, a meno che non si ricicli il server delle transazioni CICS.


È davvero interessante, grazie per la nota storica! Sai se quel paradigma fosse dovuto al fatto che liberare la memoria era troppo costoso dal punto di vista computazionale se non era necessario? O l'alternativa non era ancora stata pensata?
DilithiumMatrix

1
@zhermes: era computazionalmente impossibile, poiché il DOS semplicemente non teneva traccia delle allocazioni di memoria per i TSR. Praticamente per definizione: l'obiettivo era rimanere residenti . Se volevi che il tuo TSR liberasse parte della memoria, ma non tutta, spettava a te decidere cosa liberare.
MSalters

2
@zhermes: DOS (come CP / M, il suo antenato) non era quello che chiameresti un sistema operativo in senso moderno. In realtà era solo una raccolta di utilità di I / O che poteva essere chiamata in un modo standard in bundle con un processore di comandi che ti avrebbe permesso di eseguire un programma alla volta. Non c'era nozione di processi e la memoria non era né virtuale né protetta. I TSR erano un utile hack che poteva dire al sistema che stavano occupando fino a 64K di spazio e si agganciavano agli interrupt in modo da essere chiamati.
Blrfl

8

Come altri hanno detto, la maggior parte dei sistemi operativi recupererà la memoria allocata al termine del processo (e probabilmente altre risorse come socket di rete, handle di file, ecc.).

Detto questo, la memoria potrebbe non essere l'unica cosa di cui devi preoccuparti quando hai a che fare con new / delete (invece di raw malloc / free). La memoria allocata in new può essere recuperata, ma le cose che possono essere fatte nei distruttori degli oggetti non accadrà. Forse il distruttore di qualche classe scrive un valore sentinella in un file dopo la distruzione. Se il processo termina, l'handle del file potrebbe essere cancellato e la memoria recuperata, ma quel valore sentinella non sarebbe stato scritto.

Morale della favola, ripulisci sempre te stesso. Non lasciare che le cose penzolino. Non fare affidamento sulla pulizia del sistema operativo dopo di te. Ripulisci te stesso.


'Non fare affidamento sulla pulizia del sistema operativo dopo di te. Pulisci dopo te stesso. Questo è spesso imp ... 'molto, molto difficile' con app multithread complesse. Le perdite effettive, in cui tutti i riferimenti a una risorsa sono stati persi, sono cattive. Consentire al sistema operativo di ripulire invece di rilasciare esplicitamente riferimenti non è sempre un male e spesso l'unica strada ragionevole da seguire.
Martin James

1
In C ++, i distruttori verranno chiamati alla chiusura del programma (a meno che non si presenti qualche kill -9fan meno che brillante ...)
vonbrand

@vonbrand Vero, ma se parliamo di perdite con oggetti dinamici, quei distruttori non si verificheranno. L'oggetto che esce dall'ambito è un puntatore grezzo e il suo distruttore non è operativo. (Ovviamente, vedi gli oggetti RAII per mitigare questo problema ...)
Andre Kostur

1
Il problema con RAII è che insiste sulla deallocazione di oggetti all'uscita dal processo di cui non è effettivamente importante sbarazzarsi. Connessioni DB a cui si vuole fare attenzione, ma la memoria generale è meglio ripulita dal sistema operativo (fa un lavoro molto migliore). Il problema si manifesta come un programma che impiega assolutamente anni per uscire una volta che la quantità di memoria impaginata aumenta. È anche non banale da risolvere ...
Donal Fellows

@vonbrand: non è così semplice. std::exitchiamerò i medici, std::abortnon lo faranno, potrebbero fare eccezioni non rilevate
MSalters

7

È più probabile che dipenda dal sistema operativo che dalla lingua. In definitiva, qualsiasi programma in qualsiasi lingua otterrà la sua memoria dal sistema operativo.

Non ho mai sentito parlare di un sistema operativo che non ricicla la memoria quando un programma si chiude o si blocca. Quindi, se il tuo programma ha un limite superiore nella memoria che deve allocare, è perfettamente ragionevole allocare e non liberare mai.


Potresti rovinare l'immagine della memoria del kernel in caso di un sistema operativo semplicistico? .. Come, quei sistemi operativi senza nemmeno multitasking.
ulidtko

@ulidtko, questo sarà rovinare tutto. Se il mio programma richiede di dire 1GiB una volta ogni tanto, e lo afferra per la durata, nega l'uso di quelle risorse ad altri anche se non lo usano. Potrebbe importare oggi, oppure no. Ma l'ambiente si cambia radicalmente. Garantito.
vonbrand

@vonbrand L'uso raro di 1GiB non è normalmente un problema (a patto di avere molta memoria fisica) poiché i sistemi operativi moderni possono estrarre i bit che non sono attualmente attivi. Il problema nasce quando hai più memoria virtuale in uso attivo di quella fisica in cui ospitarlo.
Donal Fellows

5

Se il programma viene mai trasformato in un componente dinamico ("plugin") che viene caricato nello spazio degli indirizzi di un altro programma, sarà problematico, anche su un sistema operativo con una gestione ordinata della memoria. Non dobbiamo nemmeno pensare al codice che viene portato su sistemi meno capaci.

D'altra parte, il rilascio di tutta la memoria può influire sulle prestazioni della pulizia di un programma.

Un programma su cui stavo lavorando, un certo test case ha richiesto 30 secondi o più per uscire dal programma, perché stava ricorsivamente attraverso il grafico di tutta la memoria dinamica e lo rilasciava pezzo per pezzo.

Una soluzione ragionevole è quella di avere la funzionalità lì e coprirla con casi di test, ma disattivarla nel codice di produzione in modo che l'applicazione si chiuda rapidamente.


5

Tutti i sistemi operativi che meritano il titolo ripuliranno il disordine causato dal tuo processo dopo la terminazione. Ma ci sono sempre imprevisti, e se gli fosse negato l'accesso in qualche modo e qualche povero programmatore non avesse previsto la possibilità e quindi non riprovi un po 'più tardi? Sempre più sicuro ripulire te stesso SE le perdite di memoria sono fondamentali per la missione, altrimenti non vale davvero la pena IMO se tale sforzo è costoso.

Modifica: è necessario ripulire le perdite di memoria se sono nella posizione in cui si accumuleranno, come nei loop. Le perdite di memoria di cui parlo sono quelle che si accumulano in tempo costante durante il corso del programma, se hai una perdita di qualsiasi altro tipo, molto probabilmente prima o poi sarà un problema serio.

In termini tecnici se le vostre perdite sono di "complessità" di memoria O (1) vanno bene nella maggior parte dei casi, O (logn) già spiacevoli (e in alcuni casi fatali) e O (N) + intollerabili.


3

La memoria condivisa sui sistemi conformi a POSIX persiste fino a quando non viene chiamato shm_unlink o il sistema non viene riavviato.


2

Se si dispone di una comunicazione tra processi, ciò può portare ad altri processi che non completano e non consumano mai risorse a seconda del protocollo.

Per fare un esempio, una volta stavo sperimentando la stampa su una stampante PDF in Java quando ho terminato la JVM nel mezzo di un lavoro di stampa, il processo di spooling PDF è rimasto attivo e ho dovuto ucciderlo nel task manager prima di poterlo fare riprovare a stampare.

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.