Le perdite di memoria sono mai ok? [chiuso]


231

È mai accettabile avere una perdita di memoria nell'applicazione C o C ++?

Cosa succede se si alloca memoria e la si utilizza fino all'ultima riga di codice nell'applicazione (ad esempio, un distruttore di un oggetto globale)? Fino a quando il consumo di memoria non aumenta nel tempo, è corretto affidarsi al sistema operativo per liberare memoria al momento della chiusura dell'applicazione (su Windows, Mac e Linux)? Considerereste questa una vera perdita di memoria se la memoria fosse utilizzata continuamente fino a quando non fosse stata liberata dal sistema operativo.

E se una biblioteca di terze parti ti imponesse questa situazione? Rifiuterebbe di usare quella libreria di terze parti, non importa quanto altrimenti potrebbe essere?

Vedo solo uno svantaggio pratico, e cioè che queste perdite benigne si presenteranno con strumenti di rilevamento delle perdite di memoria come falsi positivi.


50
Se il consumo di memoria non aumenta nel tempo, non è una perdita.
mpez0

4
La maggior parte delle applicazioni (inclusi tutti i programmi .NET) hanno almeno alcuni buffer allocati una volta e mai liberati esplicitamente, quindi la definizione di mpez0 è più utile.
Ben Voigt,

2
Sì, se hai memoria infinita.
utente

Una perdita "benigna" (se esiste una cosa del genere) non è un falso positivo - è una perdita che è stata rilevata in modo molto corretto. Il rilevamento delle perdite, anche per le perdite che personalmente non si desidera correggere, è la ragione per cui esiste un rilevatore di perdite.
cHao,

1
@ mpez0 "Se il consumo di memoria non aumenta nel tempo, non è una perdita"? Questa non è la definizione di una perdita di memoria. Una perdita è la memoria che è stata trapelata, il che significa che non è stata liberata e non hai più alcun riferimento ad essa, quindi è impossibile per te liberarla di nuovo. Che cresca o no è irrilevante.
Mecki,

Risposte:


329

No.

Come professionisti, la domanda che non dovremmo porci è: "Va bene farlo?" ma piuttosto "C'è mai una buona ragione per farlo?" E "cercare quella perdita di memoria è un dolore" non è una buona ragione.

Mi piace mantenere le cose semplici. E la semplice regola è che il mio programma non dovrebbe avere perdite di memoria.

Anche questo mi semplifica la vita. Se rilevo una perdita di memoria, la elimino, piuttosto che passare attraverso un'elaborata struttura ad albero decisionale per determinare se si tratta di una perdita di memoria "accettabile".

È simile agli avvisi del compilatore: l'avviso sarà fatale per la mia particolare applicazione? Forse no.

Ma alla fine è una questione di disciplina professionale. Tollerare gli avvisi del compilatore e tollerare le perdite di memoria è una cattiva abitudine che alla fine mi morderà.

Per portare le cose all'estremo, sarebbe mai accettabile per un chirurgo lasciare un pezzo di attrezzatura operativa all'interno di un paziente?

Anche se è possibile che si verifichi una circostanza in cui il costo / rischio di rimuovere quel pezzo di equipaggiamento supera il costo / rischio di lasciarlo dentro, e potrebbero esserci circostanze in cui era innocuo, se ho visto questa domanda pubblicata su SurgeonOverflow.com e vedere qualsiasi risposta diversa da "no", comprometterebbe seriamente la mia fiducia nella professione medica.

-

Se una biblioteca di terzi mi imponesse questa situazione, mi indurrebbe a sospettare seriamente la qualità generale della biblioteca in questione. Sarebbe come se provassi a guidare un'auto e trovassi un paio di rondelle e dadi in uno dei portabicchieri - potrebbe non essere un grosso problema in sé, ma ritrae una mancanza di impegno per la qualità, quindi prenderei in considerazione alternative.


57
Vero e non vero allo stesso tempo. La maggior parte di noi è schiava del salario e ogni desiderio di artigianato deve fare un passo indietro ai requisiti dell'azienda. Se la libreria di terze parti presenta una perdita e consente di risparmiare 2 settimane di lavoro, potrebbe esserci un caso aziendale per utilizzarla, ecc ...
Cervo,

3
Userei comunque la libreria, se fosse qualcosa di cui avevo bisogno e non c'erano alternative decenti, ma registrerei un bug con i manutentori.
contattare l'

7
Mentre personalmente vorrei andare esattamente con la stessa risposta, ci sono programmi che difficilmente liberano memoria. Il motivo è che sono a) destinati a essere eseguiti su sistemi operativi con memoria libera eb) progettati per non funzionare a lungo. Rari vincoli per un programma in effetti, ma lo accetto come perfettamente valido.

2
Per aggiungere alcuni motivi per il controllo anticipato: quando i tuoi strumenti di debug sono inondati di perdite "benigne", come troverai quello "reale"? Se aggiungi una funzione batch e improvvisamente la perdita di 1K / ora diventa 1K / secondo?
Peter

5
Hmm "non perde la memoria" è "perfetto"?
JohnMcG,

80

Non considero una perdita di memoria a meno che la quantità di memoria "utilizzata" non continui a crescere. Avere un po 'di memoria inedita, sebbene non ideale, non è un grosso problema a meno che la quantità di memoria richiesta continui a crescere.


12
Tecnicamente, una perdita è la memoria allocata e tutti i riferimenti ad essa vengono persi. Non deallocare la memoria alla fine è semplicemente pigro.
Martin York,

17
Se hai una perdita di memoria di 1 volta di 4 GB, questo è un problema.
John Dibling,

21
Non importa se sta crescendo o no. Altri programmi non possono utilizzare la memoria se è stata allocata.
Bill the Lizard,

8
> Altri programmi non possono utilizzare la memoria se è stata allocata. Bene, il sistema operativo può sempre scambiare la tua memoria su disco e consentire ad altre applicazioni di utilizzare la RAM di cui non stavi approfittando.
Max Lybbert,

4
Se il programma ha una durata molto breve, una perdita potrebbe non essere così grave. Inoltre, sebbene NON sia l'ideale, il paging non è così costoso come alcuni qui pensano che sia, perché il programma non è interessato a quella memoria (e quindi non cambierà continuamente) - a meno che, naturalmente, non si abbia un GC ...
Arafangion,

79

Prima di tutto cerchiamo di correggere le nostre definizioni. Una perdita di memoria si verifica quando la memoria viene allocata in modo dinamico, ad esempio con malloc(), e tutti i riferimenti alla memoria vengono persi senza il corrispondente libero. Un modo semplice per realizzarne uno è questo:

#define BLK ((size_t)1024)
while(1){
    void * vp = malloc(BLK);
}

Si noti che ogni volta intorno al ciclo while (1), vengono allocati 1024 (+ overhead) byte e il nuovo indirizzo assegnato a vp; non c'è nessun puntatore rimanente ai precedenti blocchi di malloc. Questo programma è garantito fino allo scadere dell'heap e non c'è modo di recuperare la memoria di Malloc. La memoria "perde" dal mucchio, per non essere mai più vista.

Ciò che stai descrivendo, tuttavia, sembra

int main(){
    void * vp = malloc(LOTS);
    // Go do something useful
    return 0;
}

Allocare la memoria, lavorare con essa fino al termine del programma. Questa non è una perdita di memoria; non compromette il programma e tutta la memoria verrà eliminata automagicamente al termine del programma.

In generale, è necessario evitare perdite di memoria. Primo, perché come l'altitudine sopra di te e il carburante all'hangar, la memoria che è trapelata e non può essere recuperata è inutile; in secondo luogo, è molto più semplice programmare correttamente, non perdere memoria, all'inizio che trovare una perdita di memoria in un secondo momento.


Consideriamo ora alcune decine di queste allocazioni. Ora considera di dover spostare il corpo "principale" alla routine che viene chiamata più volte. Godere. - Sono d'accordo con il sentimento che non è un problema così grande in questo scenario, ma gli scenari cambiano. Come si suol dire, scrivere sempre il codice come se il ragazzo per mantenerlo sapesse dove vivi.
Peter

2
Bene, il punto è che la memoria viene mallocata e mantenuta finché il programma chiama _exit () non viene "trapelato".
Charlie Martin,

1
È una perdita di memoria e può compromettere il programma. Le allocazioni future possono fallire da questo processo perché sono sicuro che stai verificando che malloc sia tornato ovunque nullo. usando eccessivamente la memoria, come in una situazione integrata in cui il ricordo è scarso, questa potrebbe essere la differenza tra vita e morte.
MikeJ

10
Mike, non è vero. In un ambiente C conforme, la fine di main libera tutte le risorse di processo. In un ambiente embedded come quello che descrivi, potresti vedere quella situazione, ma non avresti un problema principale. Ora, garantirò che potrebbero esserci ambienti incorporati imperfetti per i quali ciò non sarebbe vero, ma poi ho visto ambienti imperfetti che non sono stati in grado di gestire correttamente anche + =.
Charlie Martin,

3
Sì, ora hai scoperto che se hai malloctroppa memoria è una brutta cosa. Non è ancora una perdita . Non è una perdita fino a quando, a meno che non si malloctratti di memoria in cui si perde il riferimento.
Charlie Martin,

39

In teoria no, in pratica dipende .

Dipende molto dalla quantità di dati su cui il programma sta lavorando, dalla frequenza con cui il programma viene eseguito e dalla sua costante esecuzione.

Se ho un programma rapido che legge una piccola quantità di dati effettua un calcolo ed esce, non si noterà mai una piccola perdita di memoria. Poiché il programma non è in esecuzione da molto tempo e utilizza solo una piccola quantità di memoria, la perdita sarà piccola e liberata quando il programma esiste.

D'altra parte, se ho un programma che elabora milioni di record e funziona a lungo, una piccola perdita di memoria potrebbe far crollare la macchina dato abbastanza tempo.

Per quanto riguarda le librerie di terze parti che presentano perdite, se causano un problema, correggi la libreria o trova un'alternativa migliore. Se non causa problemi, importa davvero?


Non so se hai letto tutta la mia domanda o meno. Sto dicendo che la memoria viene utilizzata fino alla fine dell'applicazione. Non cresce con il tempo. L'unico no no è che non c'è una chiamata per liberare / eliminare.
Imbue

2
Quindi non è davvero una perdita di memoria. Una perdita di memoria è una piccola quantità di memoria non utilizzata ma non a disposizione, questa quantità aumenta nel tempo. Quello di cui stai parlando è una goccia di memoria. Non preoccuparti di questo a meno che la tua gocciolina non sia molto grande.
vfilby,

"Se non causa problemi, importa davvero?" No, non importa affatto. Vorrei che più persone lo avessero invece di diventare religiose.
Imbue

2
@John: Questa è generalmente meno una questione di sviluppatori pigri e più una questione di software in evoluzione. Tutti commettiamo errori, i bug sono il nostro mestiere; li facciamo, li ripariamo, ecco cosa facciamo. È sempre un equilibrio tra costo iniziale e manutenzione a lungo termine, tale equilibrio non è mai semplice.
vfilby,

1
John, sono d'accordo al 100% con te. Imbum La domanda è quasi "quanto accetti". Sciatto è sciatto .. Che ne dici di lasciare un gambero dietro il monitor. puzza è puzza. Ogni volta che scaviamo, la nostra industria crolla un po '. Se sai che c'è una perdita e sai di averla causata, allora dovresti ripararla.
baash05,

37

Molte persone sembrano avere l'impressione che una volta liberata la memoria, venga immediatamente restituita al sistema operativo e possa essere utilizzata da altri programmi.

Questo non è vero. I sistemi operativi gestiscono comunemente la memoria in pagine 4KiB. malloce altri tipi di gestione della memoria ottengono le pagine dal sistema operativo e le gestiscono in modo ottimale. E 'molto probabile che free()sarà non tornare pagine al sistema operativo, in base al presupposto che il programma sarà malloc più memoria in seguito.

Non sto dicendo che free()non restituisce mai memoria al sistema operativo. Può succedere, in particolare se stai liberando grandi tratti di memoria. Ma non c'è garanzia.

Il fatto importante: se non si libera la memoria non più necessaria, si garantisce che ulteriori malloc consumeranno ancora più memoria. Ma se prima liberate, malloc potrebbe riutilizzare invece la memoria liberata.

Cosa significa in pratica? Significa che se sai che il tuo programma non richiederà più memoria d'ora in poi (ad esempio è in fase di pulizia), liberare memoria non è così importante. Tuttavia, se il programma potrebbe allocare più memoria in un secondo momento, è necessario evitare perdite di memoria, in particolare quelle che possono verificarsi ripetutamente.

Vedi anche questo commento per maggiori dettagli sul perché liberare memoria appena prima della terminazione è un male.

Un commentatore non sembra aver capito che la chiamata free()non consente automaticamente ad altri programmi di utilizzare la memoria liberata. Ma questo è il punto centrale di questa risposta!

Quindi, per convincere le persone, mostrerò un esempio in cui free () fa ben poco. Per rendere la matematica facile da seguire, farò finta che il sistema operativo gestisca la memoria in pagine da 4000 byte.

Supponiamo di assegnare diecimila blocchi da 100 byte (per semplicità ignorerò la memoria aggiuntiva che sarebbe necessaria per gestire queste allocazioni). Ciò richiede 1 MB o 250 pagine. Se poi libera 9000 di questi blocchi in modo casuale, ti restano solo 1000 blocchi, ma sono sparsi ovunque. Statisticamente, circa 5 delle pagine saranno vuote. Gli altri 245 avranno ciascuno almeno un blocco allocato in essi. Ciò equivale a 980 KB di memoria, che non può essere recuperato dal sistema operativo, anche se ora sono stati allocati solo 100 KB!

D'altra parte, ora puoi malloc () 9000 blocchi in più senza aumentare la quantità di memoria che il tuo programma sta legando.

Anche quando tecnicamentefree() potrebbe restituire memoria al sistema operativo, potrebbe non farlo. deve raggiungere un equilibrio tra il funzionamento rapido e il risparmio di memoria. Inoltre, è probabile che un programma che ha già allocato molta memoria e quindi liberato lo faccia di nuovo. Un server Web deve gestire la richiesta dopo la richiesta dopo la richiesta - ha senso mantenere un po 'di memoria "allentata" disponibile, quindi non è necessario richiedere continuamente memoria al sistema operativo.free()


1
E se, altri programmi richiedessero la memoria che il tuo programma sta trattenendo inutilmente, quindi anche se potresti non aver bisogno di altri malloc, liberare () gli spazi di memoria inutilizzati :)
MN,

2
Mi sei totalmente perso il punto. Quando si libera () memoria, altri programmi non possono usarla !! (A volte possono, in particolare se si liberano grandi blocchi di memoria. Ma spesso non possono!) Modificherò il mio post per renderlo più chiaro.
Artelius,

27

Non c'è nulla di concettualmente sbagliato nel ripulire il sistema operativo dopo l'esecuzione dell'applicazione.

Dipende molto dall'applicazione e da come verrà eseguita. Le perdite che si verificano continuamente in un'applicazione che deve essere eseguita per settimane devono essere prese in considerazione, ma un piccolo strumento che calcola un risultato senza un'esigenza di memoria troppo elevata non dovrebbe essere un problema.

C'è una ragione per cui molti linguaggi di scripting non raccolgono inutilmente i riferimenti ciclici ... per i loro schemi di utilizzo, non è un problema reale e sarebbe quindi uno spreco di risorse tanto quanto la memoria sprecata.


Informazioni sui linguaggi di scripting: Python utilizza il refounting ma ha un GC solo per liberare riferimenti ciclici. In altre lingue, il programmatore spesso evita del tutto i riferimenti esplicitamente ciclici, il che crea altri problemi.
Blaisorblade,

Le versioni precedenti di PHP non rilasciavano memoria, correvano dall'inizio alla fine crescendo in memoria - dopo i tipici 0,1 secondi di tempo di esecuzione, lo script sarebbe uscito e tutta la memoria sarebbe stata recuperata.
Arafangion,

19

Credo che la risposta sia no, non permettere mai una perdita di memoria e ho alcune ragioni che non ho visto esplicitamente dichiarate. Ci sono ottime risposte tecniche qui, ma penso che la vera risposta dipenda da ragioni più sociali / umane.

(In primo luogo, nota che, come altri hanno già detto, una vera perdita è quando il tuo programma, in qualsiasi momento, perde traccia delle risorse di memoria che ha allocato. In C, questo accade quando si malloc()passa a un puntatore e si lascia che il puntatore lasci l'ambito senza fare un free()primo.)

Il punto cruciale della tua decisione qui è l'abitudine. Quando il codice in una lingua che utilizza i puntatori, si sta andando a utilizzare i puntatori molto . E i puntatori sono pericolosi; sono il modo più semplice per aggiungere ogni tipo di grave problema al tuo codice.

Quando stai programmando, a volte sarai sulla palla e a volte sarai stanco, pazzo o preoccupato. Durante quei periodi un po 'distratti, stai programmando di più sull'autopilota. L'effetto pilota automatico non distingue tra codice unico e modulo in un progetto più ampio. Durante quei periodi, le abitudini che stabilisci sono quelle che finiranno nella tua base di codice.

Quindi no, non permettere mai perdite di memoria per lo stesso motivo per cui dovresti comunque controllare i tuoi punti ciechi quando cambi corsia anche se al momento sei l'unica macchina sulla strada. Durante i periodi in cui il tuo cervello attivo è distratto, le buone abitudini sono tutto ciò che può salvarti da disastrosi passi falsi.

Al di là del problema dell '"abitudine", i puntatori sono complessi e spesso richiedono molta potenza cerebrale per tracciare mentalmente. È meglio non "confondere l'acqua" quando si tratta di utilizzare i puntatori, soprattutto quando si è nuovi alla programmazione.

C'è anche un aspetto più sociale. Usando correttamente malloc()e free(), chiunque guardi il tuo codice sarà a tuo agio; stai gestendo le tue risorse. In caso contrario, sospetteranno immediatamente un problema.

Forse hai capito che la perdita di memoria non danneggia nulla in questo contesto, ma ogni manutentore del tuo codice dovrà risolverlo anche quando legge quel pezzo di codice. Usando free()si rimuove la necessità di considerare anche il problema.

Infine, la programmazione sta scrivendo un modello mentale di un processo in un linguaggio inequivocabile in modo che una persona e un computer possano comprendere perfettamente tale processo. Una parte vitale delle buone pratiche di programmazione non è mai l'introduzione di inutili ambiguità.

La programmazione intelligente è flessibile e generica. Una cattiva programmazione è ambigua.


Adoro l'idea dell'abitudine. Sono anche d'accordo. Se vedo una perdita di memoria, mi chiedo sempre quale altro angolo ha tagliato il programmatore. Soprattutto se è ovvio
baash05

Questa è di gran lunga la risposta migliore. Sto programmando in C ++ da 5 anni e non ho mai scritto una sola perdita di memoria. Il motivo è che non scrivo codice che tende a perdere memoria. Una buona progettazione C ++ ti viene raramente utilizzata new, in modo da eliminare subito la maggior parte delle perdite di memoria. Solo se devi assolutamente usarlo new. Il risultato newdeve essere immediatamente inserito in un puntatore intelligente. Se segui queste due regole, semplicemente non perderai mai memoria (escludendo un bug in una libreria). L'unico caso rimasto sono i shared_ptrcicli, nel qual caso devi sapere di usare weak_ptr.
David Stone,

15

Penso che nella tua situazione la risposta potrebbe essere che va bene. Ma devi assolutamente documentare che la perdita di memoria è una decisione consapevole. Non vuoi che arrivi un programmatore di manutenzione, schiaffi il tuo codice all'interno di una funzione e lo chiami un milione di volte. Quindi, se prendi la decisione che una perdita va bene, devi documentarla (IN GRANDI LETTERE) per chiunque potrebbe dover lavorare sul programma in futuro.

Se questa è una libreria di terze parti potresti essere intrappolato. Ma sicuramente documento che si verifica questa perdita.

Ma fondamentalmente se la perdita di memoria è una quantità nota come un buffer da 512 KB o qualcosa del genere, non è un problema. Se la perdita di memoria continua a crescere come ogni volta che chiami una chiamata in libreria, la memoria aumenta di 512 KB e non viene liberata, quindi potresti avere un problema. Se lo documentate e controllate il numero di volte in cui la chiamata viene eseguita, potrebbe essere gestibile. Ma allora hai davvero bisogno di documentazione perché mentre 512 non sono molti, 512 oltre un milione di chiamate sono molte.

Inoltre è necessario controllare la documentazione del sistema operativo. Se questo fosse un dispositivo incorporato, potrebbero esserci sistemi operativi che non liberano tutta la memoria da un programma che esce. Non sono sicuro, forse questo non è vero. Ma vale la pena esaminarlo.


3
"Ma devi assolutamente documentare che la perdita di memoria è una decisione consapevole." Grazie al cielo. Il punto migliore fatto finora.
pestofago il

15

Darò la risposta impopolare ma pratica che è sempre sbagliato liberare memoria a meno che ciò non riduca l'utilizzo della memoria del programma . Ad esempio, un programma che effettua una singola allocazione o serie di allocazioni per caricare il set di dati che utilizzerà per tutta la sua vita non ha bisogno di liberare nulla. Nel caso più comune di un programma di grandi dimensioni con requisiti di memoria molto dinamici (pensate a un browser Web), dovreste ovviamente liberare la memoria che non state più usando il più presto possibile (ad esempio chiudendo una scheda / documento / ecc.) , ma non c'è motivo di liberare nulla quando l'utente seleziona i clic "esci" e farlo è effettivamente dannoso per l'utente.

Perché? Per liberare memoria è necessario toccare la memoria. Anche se l'implementazione malloc del tuo sistema non memorizza metadati adiacenti ai blocchi di memoria allocati, probabilmente starai camminando su strutture ricorsive solo per trovare tutti i puntatori che devi liberare.

Supponiamo ora che il tuo programma abbia funzionato con un grande volume di dati, ma non lo ha toccato per un po '(di nuovo, il browser web è un ottimo esempio). Se l'utente esegue molte app, è probabile che una buona parte di tali dati sia stata scambiata su disco. Se esci (0) o ritorni dalla schermata principale, esce immediatamente. Ottima esperienza utente. Se ti preoccupi di provare a liberare tutto, potresti impiegare almeno 5 secondi a scambiare nuovamente tutti i dati, per poi buttarli via immediatamente. Spreco di tempo dell'utente. Spreco di durata della batteria del laptop. Spreco di usura sul disco rigido.

Questo non è solo teorico. Ogni volta che mi ritrovo con troppe app caricate e il disco inizia a bloccarsi, non prendo nemmeno in considerazione di fare clic su "esci". Arrivo a un terminale il più velocemente possibile e digito killall -9 ... perché so che "exit" peggiorerà le cose.


5
Adoro questa citazione di Raymond Chen: "L'edificio viene demolito. Non preoccuparti di spazzare il pavimento, svuotare i bidoni della spazzatura e cancellare le lavagne. E non allinearti all'uscita dell'edificio in modo che tutti possano spostarsi in / fuori dal magnete per uscire. Tutto quello che stai facendo è far aspettare alla squadra di demolizione che tu finisca questi inutili compiti di pulizia della casa. " ( blogs.msdn.microsoft.com/oldnewthing/20120105-00/?p=8683 )
Andreas Magnusson

11

Sono sicuro che qualcuno può trovare un motivo per dire Sì, ma non sarò io. Invece di dire di no, dirò che questa non dovrebbe essere una domanda sì / no. Esistono modi per gestire o contenere perdite di memoria e molti sistemi li hanno.

Ci sono sistemi della NASA su dispositivi che lasciano la terra per questo. I sistemi si riavvieranno automaticamente ogni tanto in modo che le perdite di memoria non diventino fatali per l'operazione complessiva. Solo un esempio di contenimento.


Questo è in realtà un esempio dell'invecchiamento del software. Materia di studio affascinante.
Konrad Rudolph,

Un riavvio automatico ogni tanto, eh? NASA, eh? (* guarda i vecchi CD di installazione di Microsoft Windows *) Questo spiega così tanto ...
Christian Severin

8

Se si alloca memoria e la si utilizza fino all'ultima riga del programma, questa non è una perdita. Se si alloca memoria e se ne dimentica, anche se la quantità di memoria non aumenta, è un problema. Quella memoria allocata ma inutilizzata può causare altri programmi a funzionare più lentamente o per niente.


Non proprio, poiché se non viene utilizzato, verrà semplicemente estratto. Quando l'app viene chiusa, viene rilasciata tutta la memoria.
Eclipse,

Finché è assegnato, altri programmi non saranno in grado di usarlo. Non verrà eseguito il paging se non lo si disloca.
Bill the Lizard,

Certo che lo farà - ecco di cosa si tratta la memoria virtuale. Puoi avere 1 GB di RAM effettiva, eppure avere 4 processi ciascuno allocando completamente 2 GB di memoria virtuale (purché il tuo file di paging sia abbastanza grande).
Eclipse,

Naturalmente, avrai problemi di paginazione se ognuno di questi processi sta attivamente utilizzando tutta quella memoria.
Eclipse,

Ok, capisco di cosa stai parlando ora. Se si sposta la memoria che non si utilizza, si riduce la necessità di paging. Se lo mantieni allocato, la tua applicazione lo manterrà comunque quando viene impaginato nuovamente.
Bill the Lizard,

8

Posso contare da una parte il numero di perdite "benigne" che ho visto nel tempo.

Quindi la risposta è un sì molto qualificato.

Un esempio. Se hai una risorsa singleton che ha bisogno di un buffer per archiviare una coda circolare o un deque ma non sa quanto deve essere grande il buffer e non può permettersi il sovraccarico del blocco o di ogni lettore, quindi allocare un buffer che raddoppia esponenzialmente ma se non si liberano quelli vecchi si perde una quantità limitata di memoria per coda / deque. Il vantaggio per questi è che accelerano drasticamente ogni accesso e possono cambiare gli asintotici delle soluzioni multiprocessore senza mai rischiare la contesa per un blocco.

Ho visto questo approccio usato per un grande beneficio per cose con conteggi molto chiaramente fissi come i deques di furto di lavoro per CPU, e in misura molto minore nel buffer usato per mantenere lo /proc/self/mapsstato singleton nel garbage collector conservatore di Hans Boehm per C / C ++, che viene utilizzato per rilevare i set di root, ecc.

Sebbene tecnicamente una perdita, entrambi questi casi sono limitati in termini di dimensioni e nel lavoro circolare coltivabile che ruba il caso di deque c'è una grande vittoria in termini di prestazioni in cambio di un fattore limitato di 2 aumento dell'utilizzo della memoria per le code.


1
È possibile utilizzare i puntatori di pericolo per prevenire la perdita.
Demi

8

Se allocare un mucchio di heap all'inizio del programma e non lo si libera quando si esce, questa non è una perdita di memoria in sé. Una perdita di memoria si verifica quando il programma esegue il loop su una sezione di codice e quel codice alloca heap e quindi "perde traccia" di esso senza liberarlo.

In effetti, non è necessario effettuare chiamate a free () o eliminare subito prima di uscire. Quando il processo termina, tutta la sua memoria viene recuperata dal sistema operativo (questo è certamente il caso di POSIX. Su altri sistemi operativi, in particolare quelli integrati, YMMV).

L'unica avvertenza che avrei di non liberare la memoria al momento dell'uscita è che se mai refactoring il tuo programma in modo che, ad esempio, diventi un servizio in attesa di input, fa qualunque cosa faccia il tuo programma, quindi gira in giro per aspettare un'altra chiamata di servizio, quindi ciò che hai codificato può trasformarsi in una perdita di memoria.


Mi permetto di dissentire. Questa è "una perdita di memoria in sé".
Konrad Rudolph,

Non è una perdita finché non si "perde" il riferimento all'oggetto. Presumibilmente, se la memoria viene utilizzata per tutta la durata del programma, non perde. Se il riferimento non è perduto fino all'uscita () è chiamato, allora è assolutamente non è una perdita.
sabato

Amiga DOS è stato l'ultimo O / SI esaminato che non ha ripulito i processi. Tenere presente, tuttavia, che la memoria condivisa IPC di System V può essere lasciata in giro anche se nessun processo la sta utilizzando.
Jonathan Leffler

Palm non libera memoria "trapelata" fino a quando non si esegue l'hotsync. è arrivato bene dopo l'amiga. Ho eseguito app sul mio emulatore di palma che presentava delle perdite. Non si sono mai dirette verso il mio vero palmo.
baash05,

6

questo è così specifico per il dominio che non vale la pena rispondere. usa la tua testa eccitante.

  • sistema operativo Space Shuttle: no, nessuna perdita di memoria consentita
  • codice di prova del concetto di rapido sviluppo: correggere tutte quelle perdite di memoria è una perdita di tempo.

e c'è uno spettro di situazioni intermedie.

il costo opportunità ($$$) di ritardare il rilascio di un prodotto per correggere tutto, ma le peggiori perdite di memoria, di solito riduce la sensazione di essere "sciatto o poco professionale". Il tuo capo ti paga per fargli soldi, non per avere sentimenti caldi e confusi.


2
Atteggiamento molto miope. In pratica stai dicendo che non è necessario utilizzare pratiche di programmazione fondamentalmente sane fino a quando non viene scoperto che un difetto è causato da tali pratiche. Il problema è che il software scritto usando metodi sciatti tende ad avere più difetti del software che non lo è.
John Dibling,

1
Non ci credo tutto. E la gestione della memoria è più complicata della scrittura di metodi puliti.
Dustin Getz,

1
Dustin ovviamente lavora nel mondo reale come la maggior parte di noi, dove lavoriamo continuamente contro scadenze folli per stare al passo con la concorrenza. Quindi trattare i bug dovrebbe essere fatto in modo pragmatico. Perdendo troppo tempo su bug non importanti in programmi non importanti, non riuscirai a fare le tue cose.
Wouter van Nifterick,

Il problema con questo atteggiamento è: quando inizi a correggere le perdite? "OK, è un propulsore, ma è solo carbone, non uranio. Perché riparare le perdite qui?" - Ho imparato nel mondo reale che se non fai la cosa giusta dall'inizio, sempre, non succede mai. Questo atteggiamento genera progetti che sono "completi al 99%" dopo due settimane e rimangono tali per due mesi.
Peter

5

Devi prima capire che esiste una grande differenza tra una perdita di memoria percepita e una perdita di memoria effettiva. Molto spesso gli strumenti di analisi segnalano molte aringhe rosse ed etichettano qualcosa come trapelato (memoria o risorse come maniglie ecc.) Dove in realtà non lo è. Spesso ciò è dovuto all'architettura dello strumento di analisi. Ad esempio, alcuni strumenti di analisi segnaleranno gli oggetti runtime come perdite di memoria perché non li vede mai liberati. Ma la deallocazione si verifica nel codice di arresto del runtime, che lo strumento di analisi potrebbe non essere in grado di vedere.

Detto questo, ci saranno ancora momenti in cui si avranno perdite di memoria effettive che sono molto difficili da trovare o molto difficili da risolvere. Quindi ora la domanda diventa: è mai OK lasciarli nel codice?

La risposta ideale è "no, mai". Una risposta più pragmatica può essere "no, quasi mai". Molto spesso nella vita reale hai un numero limitato di risorse e tempo per risolvere e un elenco infinito di attività. Quando uno dei compiti consiste nell'eliminare le perdite di memoria, molto spesso entra in gioco la legge dei rendimenti decrescenti. Si potrebbe eliminare il 98% di tutte le perdite di memoria in un'applicazione in una settimana, ma il restante 2% potrebbe richiedere mesi. In alcuni casi potrebbe anche essere impossibile eliminare alcune perdite a causa dell'architettura dell'applicazione senza un importante refactoring del codice. Devi soppesare i costi e i vantaggi dell'eliminazione del restante 2%.


5

In questo tipo di domande il contesto è tutto. Personalmente non sopporto le perdite, e nel mio codice faccio di tutto per correggerle se spuntano, ma non vale sempre la pena riparare una perdita e quando le persone mi pagano ogni ora che ho in occasione ho detto loro che non valeva la pena pagare per me una perdita nel loro codice. Lasciate che vi faccia un esempio:

Stavo triagolando un progetto, facendo un lavoro perfetto e correggendo molti bug. Durante l'inizializzazione delle applicazioni c'è stata una perdita che ho rintracciato e compreso appieno. Ripararlo correttamente avrebbe richiesto circa un giorno di refactoring di un pezzo di codice altrimenti funzionale. Avrei potuto fare qualcosa di confuso (come inserire il valore in un globale e catturarlo in qualche punto so che non era più in uso per liberare), ma ciò avrebbe solo causato più confusione al prossimo che doveva toccare il codice.

Personalmente non avrei scritto il codice in quel modo in primo luogo, ma la maggior parte di noi non riesce sempre a lavorare su basi di codice incontaminate e ben progettate, e talvolta devi guardare queste cose in modo pragmatico. Il tempo che mi ci sarebbe voluto per risolvere la perdita di 150 byte poteva invece essere impiegato per apportare miglioramenti algoritmici che rasavano i megabyte di RAM.

Alla fine, ho deciso che la perdita di 150 byte per un'app che utilizzava un giro di ram e funzionava su una macchina dedicata non valeva la pena correggerla, quindi ho scritto un commento dicendo che era trapelato, cosa doveva essere cambiato per risolvere e perché non ne valeva la pena al momento.


Inteligente. Soprattutto perché la perdita era durante l'inizializzazione, il che significa che non si sarebbe accumulata durante il runtime dell'applicazione.
Demi,

5

Mentre la maggior parte delle risposte si concentra su perdite di memoria reali (che non sono mai OK, perché sono un segno di codifica sciatta), questa parte della domanda mi sembra più interessante:

Che cosa succede se si alloca memoria e la si utilizza fino all'ultima riga di codice nell'applicazione (ad esempio, il decostruttore di un oggetto globale)? Fino a quando il consumo di memoria non aumenta nel tempo, è corretto affidarsi al sistema operativo per liberare memoria al momento della chiusura dell'applicazione (su Windows, Mac e Linux)? Considerereste questa una vera perdita di memoria se la memoria fosse utilizzata continuamente fino a quando non fosse stata liberata dal sistema operativo.

Se viene utilizzata la memoria associata, non è possibile liberarla prima della fine del programma. Non importa se la liberazione viene eseguita dall'uscita del programma o dal sistema operativo. Finché questo è documentato, in modo che il cambiamento non introduca perdite di memoria reali e fintanto che non ci sono distruttori C ++ o funzioni di pulizia C coinvolti nell'immagine. Un file non chiuso potrebbe essere rivelato attraverso una perditaFILE oggetto , ma un fclose () mancante potrebbe anche non far svuotare il buffer.

Quindi, tornando al caso originale, IMHO è perfettamente OK in sé, al punto che Valgrind, uno dei più potenti rilevatori di perdite, tratterà tali perdite solo se richiesto. Su Valgrind, quando si sovrascrive un puntatore senza liberarlo in anticipo, viene considerato come una perdita di memoria, perché è più probabile che accada di nuovo e causi la crescita infinita dell'heap.

Quindi, non ci sono blocchi di memoria nfreed che sono ancora raggiungibili. Uno potrebbe assicurarsi di liberarli tutti all'uscita, ma è solo una perdita di tempo in sé. Il punto è se potrebbero essere liberati prima . Ridurre il consumo di memoria è utile in ogni caso.


Wow ... qualcuno che sa cos'è una perdita di memoria.
Simon Buchan,

4

Sono d'accordo con vfilby - dipende. In Windows, trattiamo le perdite di memoria come bug relativamente gravi. Ma dipende molto dal componente.

Ad esempio, le perdite di memoria non sono molto gravi per i componenti che vengono eseguiti raramente e per periodi di tempo limitati. Questi componenti vengono eseguiti, funzionano, quindi escono. Quando escono, tutta la loro memoria viene liberata implicitamente.

Tuttavia, le perdite di memoria nei servizi o altri componenti a lungo termine (come la shell) sono molto gravi. Il motivo è che questi bug 'rubano' la memoria nel tempo. L'unico modo per ripristinarlo è riavviare i componenti. Molte persone non sanno come riavviare un servizio o la shell, quindi se le loro prestazioni di sistema ne risentono, si riavviano.

Quindi, se hai una perdita, valuta il suo impatto in due modi

  1. Per il tuo software e l'esperienza dell'utente.
  2. Al sistema (e all'utente) in termini di parsimonia con le risorse di sistema.
  3. Impatto della correzione su manutenzione e affidabilità.
  4. Probabilità di provocare una regressione altrove.

Foredecker


3. Impatto sulla manutenzione del software.
Peter

3

Anche se sei sicuro che la tua perdita di memoria "nota" non causerà il caos, non farlo. Nella migliore delle ipotesi, aprirai la strada a un errore simile e probabilmente più critico in un momento e in un luogo diversi.

Per me, chiedere questo è come chiedere "Posso spezzare il semaforo rosso alle 3 del mattino quando non c'è nessuno?". Beh, certo, in quel momento potrebbe non causare alcun problema, ma ti fornirà una leva per fare lo stesso nelle ore di punta!


3

No, non dovresti avere perdite che il sistema operativo pulirà per te. Il motivo (non menzionato nelle risposte sopra per quanto ho potuto verificare) è che non si sa mai quando il tuo main () verrà riutilizzato come funzione / modulo in un altro programma . Se main () diventa una funzione chiamata di frequente nel software di un'altra persona, questo software avrà una perdita di memoria che consuma memoria nel tempo.

KIV


3

Immagino che vada bene se stai scrivendo un programma destinato a perdere memoria (ad esempio per testare l'impatto delle perdite di memoria sulle prestazioni del sistema).


3

Sono sorpreso di vedere così tante definizioni errate di cosa sia effettivamente una perdita di memoria. Senza una definizione concreta, una discussione sul fatto che sia una cosa negativa o no non andrà da nessuna parte.

Come hanno giustamente sottolineato alcuni commentatori, una perdita di memoria si verifica solo quando la memoria allocata da un processo esce dal campo di applicazione nella misura in cui il processo non è più in grado di fare riferimento o eliminarlo.

Un processo che sta afferrando sempre più memoria non necessariamente perde. Finché è in grado di fare riferimento e deallocare tale memoria, rimane sotto il controllo esplicito del processo e non è trapelato. Il processo potrebbe essere mal progettato, specialmente nel contesto di un sistema in cui la memoria è limitata, ma non è la stessa di una perdita. Al contrario, perdere la portata di, per esempio, un buffer da 32 byte è ancora una perdita, anche se la quantità di memoria trapelata è piccola. Se ritieni che ciò sia insignificante, attendi fino a quando qualcuno avvolge un algoritmo attorno alla chiamata in libreria e lo chiama 10.000 volte.

Non vedo alcun motivo per consentire perdite nel tuo codice, per quanto piccole. I moderni linguaggi di programmazione come C e C ++ fanno di tutto per aiutare i programmatori a prevenire tali perdite e raramente c'è una buona argomentazione per non adottare buone tecniche di programmazione - specialmente se abbinate a strutture linguistiche specifiche - per prevenire le perdite.

Per quanto riguarda il codice esistente o di terze parti, in cui il controllo sulla qualità o la capacità di apportare una modifica può essere fortemente limitato, a seconda della gravità della perdita, si può essere costretti ad accettare o intraprendere azioni di mitigazione come riavviare il processo regolarmente per ridurre l'effetto della perdita.

Potrebbe non essere possibile modificare o sostituire il codice (che perde) esistente, e quindi potresti essere obbligato ad accettarlo. Tuttavia, ciò non equivale a dichiarare che è OK.


2

Non è davvero una perdita se è intenzionale e non è un problema a meno che non sia una quantità significativa di memoria o potrebbe crescere fino a diventare una quantità significativa di memoria. È abbastanza comune non ripulire le allocazioni globali durante la vita di un programma. Se la perdita si trova in un server o in un'app di lunga durata, aumenta nel tempo, quindi è un problema.


2

Penso che tu abbia risposto alla tua stessa domanda. Lo svantaggio maggiore è il modo in cui interferiscono con gli strumenti di rilevamento delle perdite di memoria, ma penso che lo svantaggio sia uno svantaggio ENORME per alcuni tipi di applicazioni.

Lavoro con applicazioni server legacy che dovrebbero essere solide, ma hanno delle perdite e i globali si intralciano con gli strumenti di rilevamento della memoria. È un grosso problema.

Nel libro "Collapse" di Jared Diamond, l'autore si chiede cosa pensasse il ragazzo che aveva abbattuto l'ultimo albero sull'isola di Pasqua, l'albero di cui avrebbe avuto bisogno per costruire una canoa per scendere dall'isola. Mi chiedo il giorno in cui molti anni fa quel primo globale fu aggiunto alla nostra base di codice. Quello era il giorno in cui avrebbe dovuto essere catturato.


2

Vedo lo stesso problema di tutte le domande sullo scenario come questa: cosa succede quando il programma cambia e improvvisamente quella piccola perdita di memoria viene chiamata dieci milioni di volte e la fine del programma si trova in un posto diverso, quindi è importante? Se si trova in una libreria, registra un bug con i manutentori della libreria, non inserire una perdita nel tuo codice.


In tal caso, l'impatto della perdita di memoria cambia ed è necessario rivalutare la priorità di collegamento della perdita.
John Dibling,

@ John: Faresti meglio a documentare almeno la perdita allora. Anche allora, non mi fiderei che qualcuno non ignori un grande commento rosso lampeggiante e comunque copia e incolla il codice che perde. Preferisco non dare a qualcuno la possibilità di farlo in primo luogo.
contattare il

2

Risponderò di no.

In teoria, il sistema operativo si pulirà dopo aver lasciato un casino (ora è maleducato, ma poiché i computer non hanno sentimenti potrebbe essere accettabile). Ma non puoi anticipare ogni possibile situazione che potrebbe verificarsi durante l'esecuzione del programma. Pertanto (a meno che tu non sia in grado di condurre una prova formale di alcuni comportamenti), la creazione di perdite di memoria è semplicemente irresponsabile e sciatta da un punto di vista professionale.

Se un componente di terze parti perde memoria, questo è un argomento molto forte contro il suo utilizzo, non solo a causa dell'effetto imminente, ma anche perché mostra che i programmatori lavorano in modo sciatto e che ciò potrebbe influire anche su altre metriche. Ora, quando si considerano i sistemi legacy questo è difficile (considerare i componenti di navigazione web: per quanto ne sappia, tutti perdono memoria) ma dovrebbe essere la norma.


2

Storicamente, ha avuto importanza su alcuni sistemi operativi in ​​alcuni casi limite. Questi casi limite potrebbero esistere in futuro.

Ecco un esempio, su SunOS nell'era di Sun 3, c'era un problema se un processo utilizzava exec (o più tradizionalmente fork e quindi exec), il nuovo processo successivo avrebbe ereditato lo stesso footprint di memoria del genitore e non poteva essere ridotto . Se un processo genitore assegnasse 1/2 concerto di memoria e non lo liberasse prima di chiamare exec, il processo figlio inizierebbe a utilizzare lo stesso 1/2 concerto (anche se non è stato assegnato). Questo comportamento è stato meglio esposto da SunTools (il loro sistema di finestre predefinito), che era un maiale di memoria. Ogni app che è stata generata è stata creata tramite fork / exec e ha ereditato l'impronta di SunTools, riempiendo rapidamente lo spazio di scambio.


2

Questo è stato già discusso fino alla nausea . In conclusione, una perdita di memoria è un bug e deve essere risolta. Se una libreria di terze parti perde memoria, ci si chiede cosa c'è di sbagliato in essa, no? Se costruissi una macchina, useresti un motore che perde occasionalmente olio? Dopotutto, qualcun altro ha realizzato il motore, quindi non è colpa tua e non puoi ripararlo, giusto?


Ma se possedevi un'auto con un motore che occasionalmente perde olio, spendi soldi per ripararlo o tieni d'occhio il livello dell'olio e lo ricarichi di tanto in tanto. La risposta dipende da tutti i tipi di fattori.
magro,

Non si tratta di possedere un'auto. Si tratta di costruire un'auto. Se ottieni una libreria di terze parti con perdite di memoria e devi assolutamente usarla, allora vivi con essa. Ma se sei tu a scrivere un sistema o una libreria, è tua responsabilità assicurarti che sia privo di bug.
Dima,

+1 trattalo come qualsiasi altro bug. (Ciò non significa "aggiustare istantaneamente" nel mio libro, ma "deve essere corretto" di sicuro)
peterchen

2

Generalmente una perdita di memoria in un'applicazione autonoma non è fatale, poiché viene ripulita all'uscita dal programma.

Cosa fai per i programmi Server progettati in modo tale da non uscire?

Se sei il tipo di programmatore che non progetta e implementa codice in cui le risorse sono allocate e rilasciate correttamente, allora non voglio avere nulla a che fare con te o il tuo codice. Se non ti interessa ripulire la tua memoria trapelata, che dire delle tue serrature? Li lasci fuori anche tu? Lasciate piccole quantità di file temporanei in giro in varie directory?

Perdere quella memoria e lasciare che il programma lo pulisca? No. Assolutamente no. È una cattiva abitudine, che porta a bug, bug e altri bug.

Pulisci dopo te stesso. Yo mamma non funziona più qui.


Ho lavorato su programmi server che utilizzano deliberatamente processi anziché thread, in modo che perdite di memoria e errori di segmentazione causino danni limitati.
magro,

Approccio interessante Sarei un po 'preoccupato per i processi che non riescono ad uscire e continuano a divorare la memoria.
EvilTeach

2

Come regola generale, se hai perdite di memoria che ritieni di non poter evitare, devi pensare di più alla proprietà dell'oggetto.

Ma alla tua domanda, la mia risposta in breve è nel codice di produzione, sì. Durante lo sviluppo, no . Questo potrebbe sembrare all'indietro, ma ecco il mio ragionamento:

Nella situazione che descrivi, in cui la memoria è conservata fino alla fine del programma, è perfettamente accettabile non rilasciarla. Una volta terminato il processo, il sistema operativo verrà comunque pulito. In effetti, potrebbe migliorare l'esperienza dell'utente: in un gioco su cui ho lavorato, i programmatori hanno pensato che sarebbe stato più pulito liberare tutta la memoria prima di uscire, causando l'arresto del programma fino a mezzo minuto! Una rapida modifica che ha appena chiamato exit () invece ha fatto scomparire immediatamente il processo e riportato l'utente sul desktop dove voleva essere.

Tuttavia, hai ragione sugli strumenti di debug: faranno un problema e tutti i falsi positivi potrebbero rendere dolorosa la ricerca della tua vera memoria. E per questo motivo, scrivi sempre il codice di debug che libera la memoria e disabilitalo al momento della spedizione.

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.