2010-01-12 6 views
7

Ho un'applicazione che sto creando che crea un gran numero di controlli di windows (pulsanti ed etichette, ecc.). Sono tutti fatti dinamicamente attraverso le funzioni. Il problema che sto avendo è che quando rimuovo i controlli e li dispongo, non vengono rimossi dalla memoria.Voglio indietro la mia memoria! Come posso disporre veramente di un controllo?

void loadALoadOfStuff() 
{ 
    while(tabControlToClear.Controls.Count > 0) 
     tabControlToClear.Controls[0].Dispose(); 
    //I even put in: 
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 
    foreach(String pagename in globalList) 
     tabControlToClear.Controls.Add(MakeATab(pagename)); 
} 

TabPage MakeATab(string tabText) 
{ 
    TabPage newT = new MakeATab(); 
    newT.Text = tabText; 
    //Fill page with controls with methods like this 
    return newT; 
} 

Ora, per qualche ragione, questo proprio non mi sta dando la mia memoria indietro, in modo che quando il processo viene eseguito 5 volte, io alla fine con un out di violazione di memoria. Sono nuovo nell'oggettare e controllare lo smaltimento ma guardare ancora attraverso la vasta rete non mi ha ancora dato alcuna indicazione, quindi se qualcuno di voi ha un'idea, sarei grato di sentirlo.

AGGIORNAMENTO: Ho osservato la creazione e la distruzione degli oggetti utente (taskmanager) e ho notato che creo una scheda, aggiungo un gestore di clic, aggiungo un pannello, aggiungo 2 pulsanti sia con gestori di clic, tooltips e backimages (I pensa che questo è dove il problema è). L'app dice che crea 8 nuovi oggetti, ma quando eseguo il mio smaltimento, rimuovo solo 4 dalla memoria. Ho cercato di rimuovere i gestori di eventi, ma sembra non fare alcuna differenza.

RISOLTO !!! Mentre aggiungevo nuovi elementi al pannello, stavo passando loro un suggerimento (stupido, ma sto imparando). Per chiunque abbia lo stesso problema, (grazie ai commenti e alle indicazioni delle persone qui sotto. Ho scoperto per fare un controllo davvero dispose (come mi rendo conto di averlo inserito così scorrettamente) è:

1: SE AVETE un suggerimento, assicurarsi che sia accessibile NON fare quel che feci ad esempio:!!!

questo è sbagliato

TabPage MakeATab(string tabText) 
{ 
    TabPage newT = new MakeATab(); 
    ToolTip myTip = new ToolTip(); 
    newT.Text = tabText; 
    //Fill page with controls with methods like this 
    myTip.SetToolTip(newT, "Something to say"); 
    return newT; 
} 

Se si esegue questa operazione, si perderà il puntatore al tooltip, e come il tooltip non è figlio dell'oggetto a cui è collegato (piuttosto, il tooltip fa un forte riferimento al controllo), quindi anche se distruggi il controllo, il tooltip che non puoi ccess mantiene vivo l'oggetto.

2: Prima di tutto, chiamare toolTip.RemoveAll(). Questo rimuove tutti i suoi legami ai controlli. Nota, se stavi usando questo suggerimento per altri controlli, hanno perso la punta dell'utensile.

3: Rimuovere tutti i controlli interni dalla control.ControlCollection base (. Se usano memoria non gestita, penso che sto facendo perche 'sta facendo il mio lavoro app così ...)

4: rimuovere eventuali gestori di eventi personalizzati.

5: infine, smaltire l'oggetto. Ho fatto una rapida funzione ricorsiva che lo fa abbastanza bene.

private void RecursiveDispose(Control toDispose) 
    { 
     while (toDispose.Controls.Count > 0) 
      RecursiveDispose(toDispose.Controls[0]); 

     if (toDispose.BackgroundImage != null) 
      BackgroundImage = null; 

     if (toDispose.GetType() == typeof(Button)) 
      toDispose.Click -= [Your Event]; 
     else if (toDispose.GetType() == typeof(TabPage)) 
      toDispose.DoubleClick -= [Your Event]; 
     else if (toDispose.GetType() == typeof(Label)) 
      toDispose.MouseMove -= [Your Event]; 

     toDispose.Dispose(); 
    } 

Questo è estremamente grezzo e probabilmente c'è un modo molto migliore di farlo, ma a meno che qualcuno può venire con esso, questo avrà a che fare. Grazie per il tuo aiuto a tutti. Potresti aver salvato il mio intero progetto.

+0

sono andato un ulteriore passo avanti, ho anche creato un metodo dispose ricorsivo per tutti gli oggetti e ancora ... Nessun ricordo indietro. private void RecursiveDispose (Control toDispose) { while (toDispose.Controls.Count> 0) RecursiveDispose (toDispose.Controls [0]); toDispose.Dispose(); } Questo, ancora una volta, rimuove visibilmente tutte le voci e cancella gli array di controllo, tuttavia non riesco ancora a recuperare memoria. –

+1

Qualcos'altro sta causando l'errore di memoria insufficiente; in generale, non è mai necessario chiamare il garbage collector. Qual è il tuo codice attuale? –

+0

Sì, ho appena notato un enorme accumulo di oggetti utente. Ma il mio codice rimuove solo alcuni di essi. Ad esempio, quando eseguo la creazione, crea 8 oggetti, ma quando dispongo gli oggetti, clearss 4 dalla memoria ... –

risposta

6

È inoltre necessario cancellare il riferimento.

while(tabControlToClear.Controls.Count > 0) 
{ 
    var tabPage = tabControlToClear.Controls[0]; 
    tabControlToClear.Controls.RemoveAt(0); 
    tabPage.Dispose(); 

    // Clear out events. 

    foreach (EventHandler subscriber in tabPage.Click.GetInvocationList()) 
    { 
     tabPage.Click -= subscriber; 
    } 
} 
+1

Un buon punto, anche se OP non ha menzionato alcun sottotipo evento. Dimenticare le assegnazioni del gestore di eventi è una causa comune di "perdite di memoria". –

+0

Caos, potrei sembrare un po 'denso (sono nuovo, abbi pietà), ma quel codice non funziona. –

+5

Forse "tabPage.Dispose();" dovrebbe essere chiamato dopo aver staccato i gestori di eventi? – lmsasu

4

In questo blocco di codice che si sta chiamando Dispose, ma non rimuovere il riferimento:

while(tabControlToClear.Controls.Count > 0) 
    tabControlToClear.Controls[0].Dispose(); 

È necessario rimuovere tutti i riferimenti al controllo (della collezione Controls più eventuali gestori di eventi registrati più eventuali altri riferimenti voi potrebbe avere) affinché un controllo sia idoneo per la garbage collection.

+0

Grazie per le informazioni, ho alcuni gestori assegnati a vari controlli, ma come dovrei creare una funzione di cancellazione ricorsiva? Ho pulsanti e etichette annidati su un pannello, posizionati su una scheda aggiunta a un controllo struttura a schede. Così fortemente annidato. Come posso gestire lo smaltimento dell'oggetto e dei suoi gestori senza conoscere il tipo? –

+0

Inoltre, ho i tooltip allegati a questi controlli, devono essere eliminati? –

0
void loadALoadOfStuff() 
{ 
    while(tabControlToClear.Controls.Count > 0) 
     tabControlToClear.Controls[0].Dispose(); 
    //I even put in: 
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 
    foreach(String pagename in globalList) 
     tabControlToClear.Controls.Add(MakeATab(pagename)); 
} 

Mi sembra che si sta riallocando tutte le istanze di tabulazione alla fine del test-metodo, quindi non è chiaramente alcun beneficio dello smaltimento loro per primi. Salta le ultime due righe e vedi se questo aiuta.

+0

Lo scopo di questa funzione è quello di rimuovere tutti i controlli, rilasciarli dalla memoria e sostituirli con quelli nuovi. Rimuovendo le ultime due righe, rimuovo il motivo per cui ho scritto la funzione, ovvero, per popolare la pagina dopo aver eliminato i vecchi oggetti non necessari. Non solo smaltimento. –

+0

Se avessi accennato all'aumento dell'uso della memoria, avrei suggerito cosa diceva ChaosPandion. Tuttavia, supponevo che ti lamentassi di un utilizzo di memoria costantemente elevato. –

+0

Nah, sto creando dinamicamente più di 1000 controlli utente ... Ho bisogno di quella memoria quando cancellano ... –

0

C'era stato un sacco di grandi risposte alla domanda così lontano che parlare di una possibile ragione per gli oggetti non vengono liberati, sono tutte cose che vale la pena.

Tuttavia quando ho questo tipo di problema, io uso un memory profiler per aiutare a rintracciare il riferimento (s) per gli oggetti, l'ultima volta che guardo profiler di memoria, il ANTS Memory Profiler (da RedGate) è stato uno dei migliori . (Fanno una coda di 14 giorni che è più che lunga per indagare su un singolo problema come questo.)

Questo è uno dei motivi per cui viene utilizzato il modello di Evento debole, tuttavia quello sarebbe un whole new question.

(La durata della UI oggetti quando si modificatore dinamicamente un interfaccia utente è un campo minato, ben fatto per taskmanager per verificare che il codice sta funzionando come previsto)

+0

Come sto scoprendo, la gestione del controllo degli utenti è molto più complessa di prima. (Dannato. Net mi ha cullato in un falso senso di comprensione.Una delle cose che penso potrebbe essere è che ho stupidamente pensato di assegnare un suggerimento a un controllo in qualche modo mettendo l'oggetto del tooltip nel controllo in modo da essere rimosso quando il controllo è stato disposto, ma no, MS doveva andare a fare è una classe strana che incapsula l'oggetto, quindi anche se ho rimosso il pulsante e ho provato a disporlo, il tooltip (che non ho più un puntatore) sta ancora tenendo l'oggetto ... Questa è una vera impresa. .. –

+0

@Psytechnic, WinForms ha molti problemi come questo in quanto è un wrapper gestito attorno all'API User32 non gestito, tuttavia è ancora meglio che provare a utilizzare User32 (o MFC) direttamente da C/C++. WPF è molto meglio perché è stato progettato fin dal primo giorno per essere utilizzato dal codice gestito. –

+0

Consiglieresti di riscrivere l'intera applicazione come app WPF? –