Newsgroup: it.comp.lang.delphi
Titolo: Oggetti in Locale e Memory Leak

Salve a tutti!
Una domanda diretta e indolore per voi :D
Ho scritto un programma che, su evento, crea degli oggetti, tutti 
concatenati tra loro mediante un link al padre, che è elemento di una 
stringlist.
Quando un evento viene triggato, a seconda dell'oggetto che l'evento mi 
passa come parametro, risalgo al padre e ne aggiorno alcune informazioni.
Per far ciò, volendo evitare cose come 
ClassCast(ClassCastII(Tstringlist.Object[i]).ObjPadre).elemento_da_aggiornare 
o simili (non fate caso alla sintassi: è solo per dare l'idea :P),
su ogni routine evento ho dichiarato in Var degli oggetti di tipo 
ClassCast e ClassCastII, li 'popolo' nelle prime righe di codice, e poi 
accedo direttamente a loro per modificare i parametri degli oggetti in 
questione.
Solo che, mi sono accorto, il programma genera un memory leakage 
impressionante.
Ora mi pongo due domande:
1) Non è che il compilatore Delphi (sospetto MOLTO grosso), all'uscita 
da una procedura, esegua il free (e non il puntamento a nil) degli 
oggetti istanziati?
Teoricamente, non dovrebbe accadere, perchè io, di fatto, non creo 
(.create) nessun oggetto in var, ma mi limito a puntare un oggetto 
esistente.
2) Dato che eseguo un gran numero di connessioni al db (circa 60 query 
al secondo stabili, più X query di controllo), non potrebbe essere che 
sia l'oggetto ADOQuery a creare questo leak? Ho notato, difatti, che 
diminuendo il numero delle query il leak si abbassa notevolmente.
Ovviamente, non sò se magari, il superiore numero di operazioni porti 
solo una maggiore frammentazione della memoria, che mi fa subito notare 
il leak comunque esistente e non dipendente dalle query.
Spero di essere stato chiaro nell'esposizione del problema (anche se 
sospetto di non esserlo stato per niente :P), e che qualcuno riesca a 
darmi una mano, perchè è da qualche giorno che cerco di venire a capo 
della faccenda, inutilmente.
Aspetto speranzoso una luce guida.
Grazie anticipatamente a tutti!
TynoSan.

 della faccenda, inutilmente.
A me sono risultate più chiare le tue domande rispetto alla 
descrizione della struttura; io controllerei il codice che hai scritto 
per verificare che tutti gli oggetti creati vengano debitamente 
distrutti al termine del loro utilizzo, seguendo il codice passo per 
passo magari concentrandosi sulla vita di un singolo oggetto.
Ciao! :-)
-- 
Marco «Alka» Breveglieri
  ABLS Team S.n.c. - Software & Web
  Via De Gasperi, 14
  42019 Scandiano (Reggio Emilia)
 
http://www.abls.it

Marco Breveglieri ha scritto:

Innanzitutto, Grazie Marco per la tua chiarissima risposta.
Pe rispondere alla tua domanda: sì, ho notato che l'utilizzo di memoria 
da parte del mio programma aumenta proporzionalmente al tempo trascorso, 
e, ad un certo punto, tutto si blocca per un errore sempre casuale
(connection persa di una query, dati che dovrebbe essere in un oggetto, 
invece, non presenti, errori di accesso alla memoria...).
Per quanto riguarda inceve il tuo suggerimento di controllare la vita di 
ogni singolo oggetto: ci provo da un po', ma è abbastanza complesso perchè
a) il programma è composto da più thread, anche se asincroni tra loro e 
non concorrenti su stesse strutture
b) il programma risponde ad eventi e, mentre magari stò seguendo una 
procedura, si genera un evento su un thread che bufferizza dati, e che 
mi rende alquanto arduo seguire l'effettivo utilizzo di memoria.
c) Non sò come fare a controllare se un oggetto creato resta lo stesso 
nel tempo e non subisce variazioni... 'assigned' serve a ben poco, e, se 
  un processo esterno o delle mie righe di codice vanno ad inficiare la 
memoria di un oggetto, me ne renderò conto solo quando andrò a leggere o 
scrivere dei dati sbagliati, dato che il puntatore all'oggetto punterà 
sempre ad esso, finchè non verrà sovrascritto.
Spero tu riesca a darmi qualche suggerimento per uscire da questa palude 
  nella quale mi sono ritrovato, magari suggerendomi qualche strumento, 
a me ignoto, per poter meglio debuggare il tutto.
Grazie ancora!
TynoSan.

 me ignoto, per poter meglio debuggare il tutto.
Indubbiamente la presenza di più thread complica un po' le cose, ma se 
i thread non sono direttamente responsabili della creazione (e della 
conseguente distruzione) degli oggetti, secondo me possiamo ignorarli.
Come hai detto tu, la procedura /Assigned/ conta ben poco, ma solo se 
ti dimentichi di impostare a *nil* la variabile che contiene il 
riferimento all'oggetto dopo che hai distrutto quest'ultimo.
Tra l'altro, non ho la possibilità di vedere il tuo codice (che 
suppongo sia voluminoso e forse "intricato"), quindi mi risulta 
difficile darti suggerimenti del tutto mirati.
Tuttavia, posso dirti che per ogni classe di cui crei un oggetto, 
dovresti avere un solo identificatore che sia qualificato come 
"riferimento ufficiale" a tale oggetto.
Devi accertarti della distruzione dell'oggetto al termine del suo 
utilizzo, a meno che il tempo non coincida con il periodo intero di 
esecuzione dell'applicazione stessa (in tal caso, la memoria viene 
rilasciata all'uscita dall'applicazione).
Quando distruggi gli oggetti, fallo richiamando la Free attraverso il 
"riferimento ufficiale" citato sopra, l'unico "contenitore" cui hai 
dato l'incarico di mantenere il riferimento agli oggetti; una volta 
chiamata la Free, poni il riferimento a *nil*, oppure usa direttamente 
la procedura /FreeAndNil/ se ti è più comoda.
Quando il "riferimento ufficiale" è uguale a *nil*, l'oggetto è da 
considerarsi distrutto e non presente in memoria; se ti rimangono 
altri riferimenti sparsi, è probabile che tu non faccia affidamento 
sulla condizione espressa sopra, cioè che tu abbia sviluppato la tua 
applicazione nell'incertezza che un oggetto sia distrutto veramente, 
ricreandone uno nuovo ogni volta (senza distruggere l'istanza precedente).
Aiutati con il costrutto *try...finally* in quelle routine dove magari 
crei un componente al volo e vuoi garantirne la distruzione prima che 
la routine stessa sia terminata.
   Object := Class.Create;
   try
     Object.Use;
   finally
     Object.Free;
   end;
Al momento, non mi viene in mente nient'altro che possa essere utile, 
ma spero di averti dato sufficienti indicazioni per avviare 
un'indagine approfondita sulla correttezza del codice che hai scritto.


 non concorrenti su stesse strutture
Hai provato ad analizzarlo con MemProof?
http://www.automatedqa.com/products/memproof.asp
-- 
Lis
Il mio sito: 
http://lis.altervista.org
FAQ di it.comp.lang.delphi: 
http://snipurl.com/faq_icld
Cos'č l'avarizia? E' un continuo vivere in miseria per paura della miseria
(San Bernardo)

 
Trovato il Bug!!!
Grazie ai vostri ottimi suggerimenti, ho:
a) verificato tramite il memproof che il mio programma, ad ogni evento, 
generava circa 60 PUNTATORI AL SECONDO
c) cercato di districarmi tra eventi e thread per ridurre l'area di 
ricerca del possibile bug...
C'ho messo un bel po' ma, alla fine, sono riuscito a beccarlo... e mi 
sono sentito alquanto stupido :P
Difatti, l'errore non stava direttamente in una mia riga di codice, ma 
una libreria che uso richiamava la funzione TdataSet.GetBookmark, che, 
al posto di riportare un result come var, fa uno STUPENDISSIMO getmem...
senza, ovviamente, freemem annessi...
Errori da poca esperienza!
Grazie ancora di tutto!.

 Grazie ancora di tutto!.
L'importante è averli risolit ;-)
Con tutto le volte che ho ricevuto aiuto, è stato un piacere esserti stato
utile
