6 messaggi dal 07 luglio 2010
Salve, sono nuovo del forum e innanzitutto è doveroso un saluto a tutti.
Espongo velocemente il mio problema.
Ho creato un mio progetto Silverlight, partendo dal template di navigazione. Dopodichè ho lasciato una unica pagina nel progetto, la pagina Home, ed ho implementato 3 tipi di layout visuali(mi servono per una galleria fotografica), derivando le 3 classi da UserControl. Per la gestione delle fotografie, ho creato una semplicissima classe, chiamata CacheManager, che non fa altro che mantenere in una HashTable le varie foto che man mano vengono richieste dall'utente, onde evitare di scaricarle ogniqualvolta vengano visitate. Lo so che il browser gestisce il tutto, ma il comportamento non mi piaceva.

Il mio problema è che, quando passo da una visuale all'altra, l'oggetto che rappresenta la "vecchia" visuale, rimane in memoria e continua a ricevere gli eventi scatenati dal CacheManager. L'unica soluzione che ho trovato è impementare l'interfaccia IDisposable(solamente per avere il metodo Dispose esposto), inserire del codice all'interno del metodo Dispose affinchè tutte le risorse "incriminate" vengano quantomento neutralizzate, e richiamare il metodo Dispose ogniqualvolta si cambia visuale o si navighi in una nuova pagina. Secondo voi va bene come soluzione o esistono metodi più "eleganti"? Ho riscontrato alcuni errori, tipo nullpointer, che ho dovuto gestire, mentre prima della modifica non si verificavano, poichè anche dopo aver chiamato il dispose, sembra che alcuni eventi rimangano nella coda di Silverligth. Scusate la lungaggine e grazie in anticipo
2.190 messaggi dal 04 marzo 2004
Contributi | Blog
"waccos" <waccos> wrote in message
news:364274@...
Salve, sono nuovo del forum e innanzitutto è doveroso un saluto a tutti. Espongo velocemente il mio problema.
Ho creato un mio progetto Silverlight, partendo dal template di navigazione. Dopodichè ho lasciato una unica pagina nel progetto, la pagina
Home, ed ho implementato 3 tipi di layout visuali(mi servono per una galleria fotografica), derivando le 3 classi da UserControl. Per la gestione delle fotografie, ho creato una semplicissima classe, chiamata CacheManager, che non fa altro che mantenere in una HashTable le varie foto
che man mano vengono richieste dall'utente, onde evitare di scaricarle ogniqualvolta vengano visitate. Lo so che il browser gestisce il tutto, ma il comportamento non mi piaceva.
Il mio problema è che, quando passo da una visuale all'altra, l'oggetto che
rappresenta la "vecchia" visuale, rimane in memoria e continua a ricevere gli eventi scatenati dal CacheManager. L'unica soluzione che ho trovato è impementare l'interfaccia IDisposable(solamente per avere il metodo Dispose
esposto), inserire del codice all'interno del metodo Dispose affinchè tutte
le risorse "incriminate" vengano quantomento neutralizzate, e richiamare il
metodo Dispose ogniqualvolta si cambia visuale o si navighi in una nuova pagina. Secondo voi va bene come soluzione o esistono metodi più "eleganti"? Ho riscontrato alcuni errori, tipo nullpointer, che ho dovuto gestire, mentre prima della modifica non si verificavano, poichè anche dopo
aver chiamato il dispose, sembra che alcuni eventi rimangano nella coda di Silverligth. Scusate la lungaggine e grazie in anticipo
Ciao,
nell'HashTable memorizzi con una chiave univoca i byte di ciascuna immagine? Il CacheManager che tipo di eventi scatena?
Stai adottando le corrette metodologie di lock?

Alessio Leoncini (WinRTItalia.com)
.NET Developer, Interactive Designer, UX Specialist, Trainer
6 messaggi dal 07 luglio 2010
ciao, innanzitutto grazie della risposta, gentilissimo.

Nell'HashTable(Dictionary in Silverlight) memorizzo semplicemente la coppia Uri/BitmapImage, con l'uri sottoforma di stringa, soluzione che mi è sembrata la più comoda. Il CacheManager scatena semplicemente 3 eventi:

CachedComplete, in seguito alla richiesta di una immagine che non è presente in cache, avvia in asincrono il download tramite la classe Webclient, al termine notifica l'evento.

CachedError, nel qual caso si verifichino errori durante il download.

CachingProgress, per notificare i progressi del download dell'immagine.

Il CacheManager è progettato in modo da eseguire un download alla volta, in caso di chiamate multiple con un download in corso, queste vengono semplicemente ignorate. All'interno del progetto è presente una sola istanza, statica, del CacheManager, così da poter gestire in modo centralizzato il tutto. Al cambio di visuale "resetto" il CacheManager, impostando a null gli eventhandler per eliminare i vari delegate registrati.

Tutte le chiamate del CacheManager avvengono sul ThreadUI, anche in questo caso c'è bisogno di controllarne la concorrenza? Comunque le uniche chiamate asincrone avvengono sul WebClient che a sua volta richiama il CacheManager che scatena l'evento associato.

Sono riuscito a notare, tramite il debugger di visual studio, che il problema principale risiede in uno specifico stato visuale, che utilizza un timer per scorrere le immagini, come uno SlideShow ad esempio. Il timer sembra non "morire" mai, continuando a richiamare l'apposito metodo per cambiare immagine. Il timer è un DispatcherTimer. L'unico modo con cui sono riuscito a risolvere è quello esposto nel precedente post, impostando a null il timer dopo averlo stoppato, però non so se come soluzione sia buona.
6 messaggi dal 07 luglio 2010
ciao, sono riuscito a scoprire l'origine del problema. Attraverso WinDebugger prima e tramite alcune stampe a video poi, in visual studio, ho scoperto che è un problema di "memory leak". Il GC non ne vuole che sapere di eliminare le istanze degli oggetti, perchè a quanto pare rimangono dei riferimenti da qualche parte, nel mio caso specifico sembra che rimangano nell'EventHandler di una classe derivata da ChildWindow.

Se qualcun'altro riscontrasse lo stesso problema, o abbia informazioni a riguardo, mi contatti quì.

Grazie
2.190 messaggi dal 04 marzo 2004
Contributi | Blog
"waccos" <waccos> wrote in message
news:364300@...
ciao, sono riuscito a scoprire l'origine del problema. Attraverso WinDebugger prima e tramite alcune stampe a video poi, in visual studio, ho
scoperto che è un problema di "memory leak". Il GC non ne vuole che sapere di eliminare le istanze degli oggetti, perchè a quanto pare rimangono dei riferimenti da qualche parte, nel mio caso specifico sembra che rimangano nell'EventHandler di una classe derivata da ChildWindow.
Se qualcun'altro riscontrasse lo stesso problema, o abbia informazioni a riguardo, mi contatti quì.
Grazie
Giusto per capire, potresti descrivere un po' di dettagli di quanto hai analizzato? Grazie.

Alessio Leoncini (WinRTItalia.com)
.NET Developer, Interactive Designer, UX Specialist, Trainer
6 messaggi dal 07 luglio 2010
certamente :-)

Tramite WinDebugger o effettuato un dump dell'heap dell'applicazione, caricata in Internet Explorer, dopo averla utilizzata per qualche minuto. L'heap presentava istanze multiple sia degli stati visuali, sia delle pagine navigate, nonostante abbia disabilitato il caching delle stesse(come descritto nel vostro libro nel capitolo dedicato), sia delle varie ChildWindow mostrate. Insomma qualsiasi oggetto istanziato all'interno della pagina o dei vari stati visuali rimaneva in memoria.

Ho effettuato il dump dei vari oggetti in memoria, per confrontarli tra di loro, ed ho notato che effettivamente tutti i campi erano ancora istanziati,tranne quelli che ho posto a null nel metodo Dispose.

Tramite il comando !gcroot ho cercato di capire, a grandi linee, dove potessero essere i riferimenti rimasti agli oggetti.

Ho inserito delle stampe sia nei costruttori che nei distruttori, per seguire il flusso delle istanze tramite visual studio. Ho scoperto che i distruttori non vengono mai richiamati.

Per ora ho risolto ripulendo i delegate dal CacheManager ad ogni cambio di visuale e ogniqualvolta si cambia pagina, ma i memory leak rimangono comunque.

In caso ti interessino altre informazioni, sono a disposizione.

Ciao
Ciao,
IDisposable non è l'ideale in questo caso perché serve per liberare le risorse non gestite, che in questo caso tu non hai.
Anche se lo chiami dal distruttore non hai nessuna garanzia che questo venga chiamato e nemmeno devi porti questo problema.
Inoltre, usando gli eventi crei un legame tra la view e il CacheManager, perciò finché questo mantiene il delegate verso la view, non partirà mai il distruttore.
Quello di cui hai bisogno è semplicemente agganciare e sganciare gli eventi una volta che non ne hai più bisogno. Puoi automatizzare il tutto con un'interfaccia ma se vuoi avere la certezza che la view vecchia non lavori più, devi staccare gli eventi.
In certi casi si usano referenze deboli, tramite WeakReference, ma non avresti comunque la certezza che il delegate non sia ancora vivo e venga eliminato dal GC.

Ciao

Il mio blog
Homepage
6 messaggi dal 07 luglio 2010
ciao ricciolo, in primis grazie per la risposta.

L'interfaccia IDisposable l'ho usata solamente per avere un metodo comune tra gli oggetti, evitando di doverne creare una io(lo so sono pigro XD), infatti il metodo Dispose lo richiamo io direttamente.
Il problema nasce proprio dal fatto che, anche sganciando gli eventhandler dal CacheManager, i riferimenti alle view rimangono comunque. Ho anche provato a svuotare la cache ad ogni cambio di visuale o di pagina, per fare dei test, ma i riferimenti rimangono comunque. Ho provato anche a forzare il Collect del GC. Non è un grosso problema, dato che a livello funzionale ho trovato il modo di risolvere il tutto(mantengo una sola istanza statica per ogni view, riciclandola al cambio di pagina o di view). In questo modo l'applicazione va, però mi intrigava cercare di trovare una soluzione definitiva ed "elegante" o cmq capire a fondo dov'è l'inghippo, anche per casi futuri. Il problema dei memory leak è poi assolutamente irrilevante per il target della mia applicazione, considerando che anche mantenendo in memoria centinaia di riferimenti leaked, la memoria occupata sarebbe comunque irrisoria.



Grazie ancora per il supporto, in caso di novità o altri confronti sono a disposizione :-)

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.