Tuesday, November 14, 2006

Memory Leaks in C++: important rules

This topic seems obvious, but it isn't!! Many memory leaks errors derive from these errors!!

So, there are some rules you must consider:

  1. In polymorphic base class you must declare destructors virtual. If you don't, the results are undefined. Typically, the derived part of the object is never destroyed (memory leak). The same analysis applies to any class lacking a virtual destructor, including std::string and all the STL container types. You do never inherit from a standard container of from any other class with a non-virtual destructor!!
  2. Destructors should never emit exception. This situation can arise the problem of simultaneously active exceptions: the result is undefined. There are three primary ways to avoid the trouble:
    • Terminate the program calling abort
    • Swallow the exception (but it suppresses that something failed)
    • Provide a regular function that gives the opportunity to react to problems that may arise (i.e., storing the result of destructor in a boolean variable)
  3. Have assignment operators return a reference to *this
  4. To make sure that a resource is always released, we need to put that resource inside an object (a resource manager) whose destructor will automatically release the resource. This idea is often called Resource Acquisition Is Initialization (RAII).
  5. A smart pointer SP is a wrapper for a resource (i.e., std::auto_ptr). It's destructor automatically calls delete on what it points to. Attention: copying auto_ptr set it to null and the copying pointer assumes sole ownership of the resource!
  6. A reference-counting smart pointer RCSP (i.e., std::tr1::shared_ptr) is a smart pointer that keeps track of how many objects point to a particular resource and automatically deletes the resource when nobody is pointing to it any longer. It also supports custom deleters: this prevents the cross-DLL problem and can be used to automatically unlock mutexes.
  7. SP and RCSP use delete, not delete[]. For arrays, look to Boost classes: boost::scoped_array and boost::shared_array.
  8. Initialize SPs in standalone statements (not in complex operations). Failure to do this can lead to subtle resource leaks when eceptions are thrown
  9. By default, C++ passes object by value:
    void viewImage(Image i) {
    i.view();
    }
    This is an expensive operation: function parameters are copies of the actual arguments! To bypass all related contructions and destructions, you have to pass by reference-to-const:
    void viewImage(const Image& i) {
    i.view();
    }
    Passing parameters by reference also avoids the slicing problem (when you don't use virtual function in polymorphic classes).
  10. It's instead more efficient to pass by value than by reference the following items:
    • built-in types (e.g. an int)
    • iterators in STL
    • function objects in STL
  11. Prefer this way to write functions that return a new object:
    inline const Image createImage(const Bitmap& bmp) {
    return Image(bmp.getName(), bmp.getData());
    }
    It's faster than you expected and prevents many errors.

  12. Remember that you incur:
    • the cost of construction when control reaches the variable's definition
    • the cost of destruction when variable goes out of scope
    You should postpone the definition until you have initialization arguments for it.
  13. In loops:
    • it's more efficient define variables outside the loop and make an assignment to it on each loop iteration
    • it's more readable define variables inside the loop. Unless you're dealing with a performance-sensitive part of your code, you should using this approach.
  14. Don't inline everything! Consider the rule of 80-20: a typical program spends 80% of its time executing only 20% of its code. You goal as a software developer is to identify the 20% of your code.
This work comes from the Scott Meyers's book "Effective C++ - Third Edition"
Very interesting the George Belotsky's article.

7 comments:

Anonymous said...

Very complete article! Thanks

Alberto Negri said...

Già un italiano che scrive in inglese per darsi un certo tono lascia il tempo che trova, ma dal punto di vista tecnico mi chiedo se l'autore, questo meraviglioso informatico poliglotta, sappia veramente cosa sia un memory leak. Un memory leak è un "buco" di memoria dovuto ad aver fatto una operazione di istanziazione di un'oggetto nello heap(malloc,calloc,realloc in C; new in C++. Tanto per fare degli esempi) senza aver poi liberato la memoria una volta che l'oggetto non serve più.
Mi si può spiegare cosa centra dove definisco le variabili di un loop con i memory leak? E il modo in cui passo ad una funzione i parametri(per riferimento o per valore) cosa c'entra? Anche passando i dati per riferimento non è necessariamente detto che si possa incorrere in un memory leak. Visto che non si dice che è stata fatta una "new" posso aver ottenuto l'indirizzo di una variabile tramite l'apposito operatore di deferenziazione e in quel caso non c'è alcuna variabile sullo heap e di conseguenza non c'è alcuna possibilità di incorrere in un memory leak.

A me più che un articolo sui memory leak sembra un articolo su generici consigli di programmazione...bah meglio parlare come si mangia. Di gente che getta fumo negli occhi ce n'è già abbastanza tra gli assicuratori.

Ricibald said...

Alcune risposte ai tuoi commenti:

1. Non mi ritengo un "meraviglioso informatico poliglotta", ma solo una persona che ha voglia di mettere online le mie conoscenze. Scrivo inglese non per darmi un tono, ma perché so benissimo che è la lingua universale (specialmente in campo informatico) e, per quanto possibile, cerco di farmi capire così. Sicuramente ci saranno tanti errori, ma almeno provo a diffondere ciò che so a una schiera più ampia di persone

2. So benissimo cos'è un memory leak. Probabilmente quello che dici hai ragione: ho mescolato il problema dei memory leak con quello dell'efficienza. Ma permettimi di dire che i punti
1,2,4,5,6,7,8 riguardano da vicino i memory leak e i problemi annessi. In seguito mi sono "lasciato andare" ed ho parlato più di aspetti metodologici per un codice efficiente.
Ma anche tali aspetti sono importanti: se analizzi il punto 11, non è semplicemente un consiglio di programmazione, ma il modo raccomandato da Scott Meyers per impedire una grande serie di memory leak derivanti da una "new" mai deallocata.

Potevo giustificare ogni punto affermando che ogni "consiglio generico di programmazione" ha in realtà forti ripercussioni su potenziali errori di memory leak. E dato che i memory leak avvengono soprattutto per un mancanza di regole generali di programmazione, ho cercato (se mi perdoni) di fare questo.

Infine, per quanto riguarda il loop o il riferimento per variabile, devo ammettere che in quel punto sono uscito fuori tema senza volere ed ho parlato più che altro di efficienza (ma mi sembra che ho chiarito nell'articolo che in quei punti si parlava di efficienza).

Ma ti ripeto che non c'è assolutamente voglia di manifestare qualcosa. Sia perché non ne ho bisogno, sia perché di manifestare qualcosa non so che farmene. Se c'è qualcosa che non ti piace nell'articolo "ti prego" di comunicarmelo direttamente (hai la mia email nel profilo) così da perfezionarlo se ci sono errori!!

Non c'è bisogno di manifestare il proprio disappunto a tutti costi. La cosa più costruttiva che potevi fare era comunicarmi direttamente la cosa (o scrivere qui il tuo disappunto con toni meno sarcastici). A differenza di ciò che pensi, io non ho nessun problema a ricevere consigli
. Tu forse hai problema a darli...

Alberto Negri said...

Beh se il farti notare che il tuo articolo è fuori tema è un modo sbagliato di comunicarti un "suggerimento", suppongo lo sarebbe stato scritto in ogni altra "salsa" (senza ironia, in via privata, per piccione viaggiatore, eccetera). Intendo dire, se permetti che gli utenti del tuo blog ti rispondano, implicitamente consenti loro di non farti solo apprezzamenti positivi; altrimenti esplicitalo: "se volete farmi dei bei complimenti allora scrivete un commento" e non "lascia il tuo commento". E' fuorviante! Ci fa credere di avere addiritttura il diritto di parola!

Comunque il fatto resta. I consigli di buona programmazione sono una cosa, i memory leak, per quanto siano errori di programmazione, un'altra. Se assumiamo per vera la tua ipotesi allora ogni bug è un errore dovuto al mancato rispetto delle regole di buona programmazione.

Infine per quanto riguarda l'inglese oggi, è l'equivalente del latino cinquant'anni fa; ossia la lingua dei "dotti". Usata esclusivamente per dire "io sono colto e tu no"; ossia gettar fumo negli occhi.
Un buon informatico/programmatore/ecc non deve necessariamente conoscere l'inglese,
nè è vero il contrario; ossia che una persona che conosce bene l'inglese sia un buon informatico/programmatore/ecc. Visto che siamo (entrambi) italiani battiamoci per questo oramai dimenticato orgoglio nazionale! E iniziamo ad usare la nostra testa.
Detto questo vi/ti lascio con un caloroso beeeeelato e i migliori auguri per il futuro.
Alberto

P.S.: Avere una critica negativa non significa essere "cestinati", significa solo essere umani e come tali poter sbagliare.

Ricibald said...

Ti ho già detto nella mia risposta che non ho problemi a ricevere consigli, tutt'altro.

Non ho alcuna pretesa di essere il portatore di una conoscenza stabilita e qualunque critica ti assicuro che è benvenuta. La tua "aspra critica" mi sarà preziosa per un successivo articolo per non ripetere gli stessi errori e ti ringrazio.

Secondo me nessun consiglio/critica è giusta o sbagliata. Sono contrariato se leggo frasi tipo "Meraviglioso informatico poliglotta" o "scrive in inglese per darsi un tono" o "meglio parlare come si mangia".

Ti ripeto che non sono contrariato se leggo critiche sul mio articolo. Possono anche essere critiche dure: le considero uno strumento per migliorare. Ma critiche sarcastiche mi sembrano veramente fuori luogo.

Alberto Negri said...

E' il bello della diretta. Altresì detto avere a che fare con gente che non viene pagata per dirti: "bravo".
Diversamente da quanto ci ha oggi abituato la televisione e lo spettacolo in genere in cui il pubblico è pagato per applaudire.
Togli i commenti se non ami le "aspre critiche", oppure metti lo zucchero.

Alberto

Anonymous said...

Volevo dire alcune cose: è vero che l'articolo è un pò fuorviante ma è molto interessante. Probabilmente basterebbe cambiare un pò titolo e introduzione.

Per quanto riguarda l'italiano è una lingua fantastica che amo parlare, ma scordiamoci di utilizzarla come metodo per comunicare le nostre idee sulla rete. Non so quanti attrezzi tecnologici ho acquistato dove erano presenti 15 lingue compreso un dialetto suomi e l'italiano era abbondantemente trascurato.

Ultima cosa nei commenti di un blog uno ci può scrivere ciò che vuole, e può anche essere moderato se i commenti non sono per esempio inerenti all'argomento trattato... ma questo non era il caso. Viva il libero scambio di opinioni.