Programmazione orientata agli oggetti in ANSI-C. Metodi di classe per risolvere il memory leakage

Le moderne workstation hanno parecchia memoria. Se un programma perde le tracce di un byte qua e là non fa di solito una gran differenza. Tuttavia, questi memory leaks sono di solito sintomo di errori negli algoritmi.

A volte i programmi si comportano in modo inaspettato di fronte ad un input strano oppure, ed è sicuramente un caso peggiore, il programma è stato inavvertitamente costruito per interrompere le connessioni con la memoria allocata dinamicamente. In questo capitolo discuteremo una tecnologia generale disponibile nell'ambito della programmazione object oriented che può essere usata fra le altre cose, per combattere il memory leakage.

Tutte le risorse acquisite da un programma dovrebbero essere adeguatamente riciclate. La memoria dinamica è una risorsa e i programmi dovrebbero essere controllati per evitare memory leaks. Ad esempio, consideriamo cosa succede quando facciamo un errore di sintassi mentre usiamo il calcolatore sviluppato nel capitolo 3 e 5.

$ value
(3 * 4) — —
bad factor: ’’ 0x0

L'algoritmo ricorsivo alla base del calcolatore cerca di costruire un albero delle espressioni. Se qualcosa va storto, la funzione error() usa longjmp() per eliminare qualsiasi cosa si trovi nello stack e continua l'esecuzione del programma principale. Tuttavia, lo stack contiene parti dell'albero dell'espressione che era già stato costruito. Se c'è un errore di sintassi, queste parti sono perse ed ecco che abbiamo un memory leak. Questo è un problema standard nella realizzazione di interpreti.

NeXTSTEP fornisce un'applicazione semplice MallocDebug che può essere usata per localizzare almeno alcuni dei problemi più seri. Se linkiamo value con -IMALLOCDEBUG, le versioni standard di malloc() e le funzioni relative sono rimpiazzate da un modulo che può comunicare con l'applicazione MallocDebug. Iniziamo MallocDebug dopo value, collegando i due, e premiamo un bottone Leaks quando riceviamo il primo messaggio di errore. Sfortunatamente l'output è semplicemente

No nodes

MallocDebug usa un metodo semplicissimo per verificare la presenza di leaks: ha una lista delle aree di memoria allocate e scandisce le parole nel task del client per vedere se puntano ad aree allocate. Solo le aree in cui non punta alcuna parola del task del client sono considerate essere memory leaks. Per l'input

(3 * 4) — —

sum() avrà il primo sottoalbero costruito da product() prima che factor() esegua fino alla fine della linea di comando. Tuttavia, quando error() raccoglie lo stack dopo l'esecuzione di factor() e lo ripassa a main(), l'indirizzo della radice di questo sottoalbero è ancora nella variabile locale result di sum() e, per puro caso, non viene sovrascritto in longjmp(). I nodi rimanenti sono collegati alla radice, ossia dal punto di vista di MallocDebug, tutti i nodi possono essere ancora raggiunti. Tuttavia, se digitiamo un'altra espressione il vecchio stack viene sovrascritto e MallocDebug troverà il leak:

value:
$ value
(3 * 4) — —
bad factor: ’’ 0x0
1 + 3
4
MallocDebug:
Zone: Address: Size: Function:
default 0x050ec35c 12 mkBin, new, product, sum,
factor, product, sum, stmt

Se value fosse compilato con le informazioni di debug, potremmo far partire un debugger in una seconda finestra e investigare sul leak:

$ gdb value
GDB is free software ...
(gdb) attach 746
Attaching program `value’, pid 746
0x5007be2 in read ()
(gdb) print * (struct Bin *) 0x050ec35c
Reading in symbols for mathlib.c...done.
$1 = {
type = 0x8024,
left = 0x50ec334,
right = 0x50ec348
}
(gdb) print process(0x050ec35c)
Reading in symbols for value.c...done.
$3 = void
(gdb)

Il debugger GNU può essere collegato al processo in esecuzione. Con print possiamo visualizzare il contenuto del nodo che stiamo perdendo se andiamo a copiare l'indirizzo da MallocDebug e se forniamo il tipo appropriato: mkBin() era la funzione che chiamava malloc() in origine, quindi dobbiamo riuscire ad ottenere una struct Bin. Come mostrato dall'output, print può persino chiamare un metodo come process() in value e visualizzare il risultato. L'output da process() appare nella finestra in cui value è in esecuzione:

$ value
(3 * 4) — —
bad factor: ’’ 0x0
1 + 3
4
12

Ed ecco quindi il memory leak vivo e vegeto.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend