2010-01-21 12 views
8

Dovrebbe essere facile. Diciamo che ho il seguente codice:C# .NET smaltimento oggetti

void Method() 
{ 
    AnotherMethod(new MyClass()); 
} 

void AnotherMethod(MyClass obj) 
{ 
    Console.WriteLine(obj.ToString()); 
} 

Se io chiamo "Metodo()", cosa succede all'oggetto MyClass che è stato creato nel processo? Esiste ancora nello stack dopo la chiamata, anche se nulla lo sta usando? O viene rimosso immediatamente?

Devo impostarlo su null per far sì che GC lo noti più rapidamente?

+0

A differenza di C++, gli oggetti non vivono nello stack in lingue come Java/C#; solo i riferimenti agli oggetti fanno, gli oggetti stessi sono nell'heap. –

risposta

7

Se chiamo "Metodo()", cosa succede all'oggetto MyClass creato nel processo?

Viene creato nell'heap GC. Quindi un riferimento alla sua posizione nell'heap viene posizionato nello stack. Quindi avviene la chiamata a AnotherMethod. Quindi viene chiamato il metodo ToString dell'oggetto e il risultato viene stampato. Quindi restituisce AnotherMethod.

Esiste ancora nello stack dopo la chiamata, anche se non lo sta utilizzando?

La tua domanda è ambigua. Con "la chiamata" intendi la chiamata a Method o AnotherMethod? Fa la differenza perché a questo punto, se la memoria dell'heap è un candidato per la garbage collection dipende dalla compilazione o meno delle ottimizzazioni. Ho intenzione di modificare leggermente il tuo programma per illustrare la differenza. Supponiamo che hai avuto:

void Method() 
{ 
    AnotherMethod(new MyClass()); 
    Console.WriteLine("Hello world"); 
} 

con le ottimizzazioni al largo, a volte in realtà generare codice che sarebbe come questo:

void Method() 
{ 
    var temp = new MyClass(); 
    AnotherMethod(temp); 
    Console.WriteLine("Hello world"); 
} 

Nella versione non ottimizzata, il tempo di esecuzione sarà effettivamente scegliere di trattare l'oggetto come non- raccoglibile fino al ritorno di Method, dopo WriteLine. Nella versione ottimizzata, il runtime può scegliere di trattare l'oggetto come oggetto da collezione non appena viene restituito AnotherMethod, prima di WriteLine.

La ragione della differenza è perché rendere la vita degli oggetti più prevedibile durante le sessioni di debug spesso aiuta le persone a capire i loro programmi.

Oppure viene rimosso immediatamente?

Nulla viene raccolto immediatamente; il garbage collector funziona quando sembra che dovrebbe funzionare. Se hai bisogno di risorse come l'handle di un file da ripulire immediatamente quando hai finito, usa un blocco "using". In caso contrario, quindi lasciare che il garbage collector decida quando raccogliere la memoria.

Devo impostarlo su null per far sì che GC lo noti più rapidamente?

Devi impostare quale valore su null? Quale variabile avevi in ​​mente?

Indipendentemente dal fatto che non si è fare fare qualsiasi cosa per far funzionare il garbage collector. Funziona da solo bene senza chiedere conferma.

Penso che tu stia pensando troppo a questo problema. Lascia che il garbage collector faccia la sua cosa e non ti preoccupare. Se stai avendo un problema reale con la memoria che non viene raccolta in modo tempestivo, mostraci un codice che illustra questo problema; altrimenti, rilassati e impara ad amare il recupero automatico della memoria.

+0

Ottima risposta! Quando ho detto "set to null", nel tuo secondo esempio (ottimizzazioni disattivate), sarebbe lo stesso di "temp = null" dopo averlo usato in AnotherMethod. Motivo Questo problema è che ho riscontrato un'eccezione di serializzazione impossibile nel mio codice. Diciamo che Method() esiste in "TopClass". Ho segnato "TopClass" come serializzabile. MyClass non può essere serializzato. Stavo pensando che GC non agiva abbastanza velocemente e un oggetto non serializzabile (MyClass) è stato lasciato all'interno di uno serializzabile. Provare a serializzare "TopClass" genererebbe quindi un'eccezione. – Nick

+2

Penso che la tua ipotesi non sia in grado di spiegare realmente il problema della serializzazione. C'è forse un * campo * del tuo oggetto che contiene un riferimento ad un oggetto non serializzabile? –

+0

D'accordo con Eric Lippert sulla serializzazione (e chi sono io per non essere d'accordo). La serializzazione riguarda i dati di un tipo, non il suo codice. Qualunque cosa "locale" a un metodo non dovrebbe avere importanza. –

13

Al termine della chiamata al metodo, l'oggetto MyClass è attivo ma non vi sono riferimenti ad esso da un valore radice. Quindi vivrà fino alla prossima volta che il GC verrà eseguito dove verrà raccolto e recuperato la memoria.

Non c'è davvero nulla che tu possa fare per accelerare questo processo se non per forzare un GC. Tuttavia questa è probabilmente una cattiva idea. Il GC è progettato per ripulire tali oggetti e ogni tentativo che fai per renderlo più veloce probabilmente si tradurrà in un rallentamento complessivo. Scoprirai anche che un GC, pur eseguendo correttamente la pulizia degli oggetti gestiti, potrebbe non ridurre effettivamente la memoria nel tuo sistema. Questo perché il GC lo tiene in giro per un uso futuro. È un sistema molto complesso che in genere è meglio lasciare ai propri dispositivi.

+1

Non guardare l'uomo dietro la tenda! :) Mi piace davvero lasciare tutto ciò che il GC può gestire. Una cosa in meno di cui preoccuparsi. –

-4

Per quanto ne so, l'oggetto è valido solo all'interno del contesto del metodo. Dopo che il metodo "Metodo()" è stato eseguito, viene aggiunto alla coda di smaltimento.

+0

Cos'è questa "dispose queue" di cui stai parlando? Non ne ho mai sentito parlare. Puoi spiegare cosa intendi? –

+0

Intendo che l'oggetto è contrassegnato come "disponibile per lo smaltimento". Ho letto che quegli oggetti sono aggiunti a una coda per essere smaltiti dal GC. – lluismontero

+0

Penso che tu stia pensando ai finalizzatori in sospeso. La tua dichiarazione, in senso stretto, è corretta; il riferimento all'oggetto è logicamente valido solo fino alla conclusione del metodo. E in un momento * imprecisato * in futuro dopo che il metodo è stato fatto, il garbage collector scoprirà che l'oggetto non è più rootato, lo contrassegna per la finalizzazione, lo finalizza e restituisce la memoria all'heap del GC. Ma non è corretto caratterizzare il GC come fare la coda aggressivamente su oggetti morti quando il controllo lascia ogni metodo; non è affatto come funziona. –

3

In realtà, l'istanza verrà dichiarata sullo heap, ma dare un'occhiata all'articolo di Eric Lipper, The Stack is an Implementation Detail.

In ogni caso, poiché non ci saranno più riferimenti esempio dopo la funzione esegue, sarà (o, più precisamente, può essere) soppresso dal garbage collector ad un certo punto indefinito nel futuro. Per quanto riguarda esattamente quando ciò accade, è indefinito, ma anche tu (essenzialmente) non devi preoccuparti di ciò; il GC ha algoritmi complicati che aiutano a determinare cosa e quando raccogliere.

2

In C# il nuovo MyClass() ha l'ambito solo per vivere all'interno di Method() mentre è attivo. Una volta terminato l'esecuzione di AnotherMethod(), è fuori portata e viene sradicato. Rimane quindi nell'heap fino a quando il GC esegue il ciclo di raccolta e lo identifica come blocco di memoria senza riferimento. Quindi è ancora "vivo" nell'heap ma è inaccessibile.

3

Esiste ancora nella pila dopo la chiamata

semantica è importante qui. Hai chiesto se esiste ancora nello stack dopo la chiamata al metodo. La risposta è "no". E 'stato rimosso dalla pila. Ma questa non è la storia finale. L'oggetto esiste ancora, non è più rootato. Non verrà distrutto o raccolto fino a quando non verrà eseguito il GC. Ma a questo punto non ti interessa più. Il GC è molto più bravo a decidere quando collezionare qualcosa di quello che tu o io.

Devo impostarlo su null per far sì che GC lo noti più rapidamente?

Non c'è quasi mai una buona ragione per farlo, comunque. L'unica volta che ti aiuta è se hai un metodo di esecuzione molto lungo e un oggetto con cui hai finito presto che altrimenti non andrà fuori campo fino alla fine del metodo. Anche in questo caso, impostarlo su null aiuterà solo nel raro caso in cui il GC decida di eseguire durante il metodo. Ma in questo caso probabilmente stai facendo anche qualcos'altro.

+2

In realtà, il GC CLR * può * ripulire una variabile non nulla mentre un metodo è in esecuzione se può determinare che la variabile non verrà utilizzata per il resto del metodo. Questo è il motivo per cui 'GC.KeepAlive()' deve esistere (anche se non dovresti mai usare quel metodo a meno che tu non *, davvero * abbia bisogno di). Quindi, impostare una variabile su null non dovrebbe fare alcuna differenza. –

1

Il GC tiene traccia di quali oggetti possono ancora essere referenziati più avanti nel codice. Quindi, a intervalli, controlla se ci sono oggetti ancora in vita che non possono essere più referenziati più avanti nel codice e li puliscono.

I meccanismi di questo sono alquanto complessi e quando queste raccolte avverranno dipende da una varietà di fattori. Il GC è progettato per fare queste raccolte nel momento più ottimale (che può stabilire) e così, mentre è possibile costringerlo a fare una raccolta, è quasi sempre una cattiva idea.

L'impostazione della variabile su null avrà un effetto generale molto ridotto sulla rapidità con cui l'oggetto viene gestito. Mentre può, in alcuni piccoli casi d'angolo, essere di beneficio, non vale la pena di sporcare il codice con assegnazioni ridondanti che non influenzeranno le prestazioni del codice e solo la leggibilità del danno.

Il GC è progettato per essere il più efficace possibile senza che tu debba pensarci. Per essere onesti, l'unica cosa di cui hai davvero bisogno di essere attenti è stare attenti nell'assegnare oggetti molto grandi che rimarranno in vita per molto tempo, e in generale è abbastanza raro nella mia esperienza.

Problemi correlati