.net - sharp - proprietà c




Come disporre una classe in.net? (14)

Il garbage collector .NET alla fine libererà la memoria, ma cosa succederebbe se volessi riavere quella memoria immediatamente? Quale codice è necessario utilizzare in una classe MyClass per chiamare

MyClass.Dispose()

e liberare tutto lo spazio utilizzato da variabili e oggetti in MyClass ?


È possibile disporre solo di istanze che implementano l'interfaccia IDisposable.

Per forzare un garbage collector a liberare immediatamente la memoria (non gestita):

GC.Collect();  
GC.WaitForPendingFinalizers();

Questa è normalmente una cattiva pratica, ma c'è un bug nella versione x64 del framework .NET, ad esempio, che rende il GC strano in alcuni scenari, e quindi potresti voler farlo. Non so se il bug è stato risolto ancora. Qualcuno sa?

Per disporre una classe fai questo:

instance.Dispose();

o in questo modo:

using(MyClass instance = new MyClass())
{
    // Your cool code.
}

che tradurrà in fase di compilazione per:

MyClass instance = null;    

try
{
    instance = new MyClass();        
    // Your cool code.
}
finally
{
    if(instance != null)
        instance.Dispose();
}

È possibile implementare l'interfaccia IDisposable in questo modo:

public class MyClass : IDisposable
{
    private bool disposed;

    /// <summary>
    /// Construction
    /// </summary>
    public MyClass()
    {
    }

    /// <summary>
    /// Destructor
    /// </summary>
    ~MyClass()
    {
        this.Dispose(false);
    }

    /// <summary>
    /// The dispose method that implements IDisposable.
    /// </summary>
    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// The virtual dispose method that allows
    /// classes inherithed from this one to dispose their resources.
    /// </summary>
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources here.
            }

            // Dispose unmanaged resources here.
        }

        disposed = true;
    }
}

@Curt Hagenlocher - è di nuovo in testa. Non ho idea del motivo per cui così tanti hanno votato su quando è sbagliato.

IDisposable è per risorse gestite .

I finalizzatori sono per risorse non gestite .

Finché utilizzi solo risorse gestite sia io che Jonj Limjap siamo del tutto corretti.

Per le classi che usano risorse non gestite (e tenete a mente che la stragrande maggioranza delle classi .Net non lo fanno), la risposta di Patrik è completa e buona pratica.

Evita di usare GC.Collect - è un modo lento per gestire le risorse gestite e non fa nulla con quelle non gestite a meno che tu non abbia correttamente costruito i tuoi ~ Finalizzatori.

Ho rimosso il commento del moderatore dalla domanda originale in linea con https://.com/questions/14593/etiquette-for-modifying-posts


@Keith:

IDisposable è per risorse gestite.

I finalizzatori sono per risorse non gestite.

Scusa ma è solo sbagliato. Normalmente, il finalizzatore non fa nulla. Tuttavia, se il pattern di eliminazione è stato implementato correttamente, il finalizzatore tenta di richiamare Dispose .

Dispose ha due posti di lavoro:

  • Risorse non gestite gratuite e
  • risorse gestite nidificate gratuite.

E qui la tua affermazione entra in gioco perché è vero che, mentre finalizzi, un oggetto non dovrebbe mai provare a liberare risorse gestite annidate in quanto potrebbero essere già state liberate. Deve comunque liberare risorse non gestite.

Tuttavia, i finalizzatori non hanno altro compito che chiamare Dispose e dirgli di non toccare oggetti gestiti. Dispose , quando viene chiamato manualmente (o tramite Using ), libera tutte le risorse non gestite e passa il messaggio Dispose agli oggetti nidificati (e ai metodi della classe base) ma questo non libererà mai alcuna memoria (gestita).


Dai un'occhiata a questo article

L'implementazione del pattern Dispose, IDisposable e / o un finalizzatore non ha assolutamente nulla a che fare quando viene recuperata la memoria; invece, ha tutto a che fare con il dire al GC come recuperare quella memoria. Quando chiami Dispose () non stai interagendo in alcun modo con il GC.

Il GC funzionerà solo quando determina la necessità di (chiamata pressione di memoria) e quindi (e solo allora) affiderà la memoria per gli oggetti non utilizzati e comprimerà lo spazio di memoria.

Potresti chiamare GC.Collect () ma non dovresti farlo a meno che non ci sia un buon motivo per (che è quasi sempre "Mai"). Quando imponi un ciclo di raccolta fuori banda come questo, in effetti il ​​GC fa più lavoro e alla fine può danneggiare le prestazioni delle tue applicazioni. Per tutta la durata del ciclo di raccolta GC, la tua applicazione è effettivamente in uno stato congelato ... più cicli GC vengono eseguiti, più tempo verrà sospeso dall'applicazione.

Ci sono anche alcune chiamate API Win32 native che puoi fare per liberare il tuo working set, ma anche quelle dovrebbero essere evitate a meno che non ci sia un buon motivo per farlo.

L'intera premessa dietro un runtime di gargbage raccolto è che non è necessario preoccuparsi (tanto) di quando il runtime alloca / rilascia la memoria effettiva; devi solo preoccuparti di assicurarti che l'oggetto sappia come ripulire se stesso quando richiesto.


IDisposable non ha nulla a che fare con la liberazione della memoria. IDisposable è uno schema per liberare risorse non gestite - e la memoria è decisamente una risorsa gestita.

I collegamenti che puntano a GC.Collect () sono la risposta corretta, sebbene l'uso di questa funzione sia generalmente scoraggiato dalla documentazione di Microsoft .NET.

Edit: Avendo guadagnato una notevole quantità di karma per questa risposta, sento una certa responsabilità nel elaborarlo, per evitare che un nuovo arrivato alla gestione delle risorse .NET abbia l'impressione sbagliata.

All'interno di un processo .NET, esistono due tipi di risorse: gestite e non gestite. "Gestito" significa che il runtime ha il controllo della risorsa, mentre "non gestito" significa che è responsabilità del programmatore. E c'è davvero solo un tipo di risorsa gestita che ci interessa in .NET oggi - la memoria. Il programmatore dice al runtime di allocare memoria e dopo di ciò spetta al runtime calcolare quando la memoria può essere liberata. Il meccanismo utilizzato da .NET per questo scopo è chiamato garbage collection e puoi trovare molte informazioni su GC su Internet semplicemente usando Google.

Per gli altri tipi di risorse, .NET non sa nulla di pulirli, quindi deve fare affidamento sul programmatore per fare la cosa giusta. A tal fine, la piattaforma offre al programmatore tre strumenti:

  1. L'interfaccia IDisposable e l'istruzione "using" in VB e C #
  2. finalizzatori
  3. Lo schema IDisposable implementato da molte classi BCL

Il primo di questi consente al programmatore di acquisire in modo efficiente una risorsa, usarla e quindi rilasciarla tutta nello stesso metodo.

using (DisposableObject tmp = DisposableObject.AcquireResource()) {
    // Do something with tmp
}
// At this point, tmp.Dispose() will automatically have been called
// BUT, tmp may still a perfectly valid object that still takes up memory

Se "AcquireResource" è un metodo factory che (ad esempio) apre un file e "Dispose" chiude automaticamente il file, allora questo codice non può perdere una risorsa file. Ma la memoria per l'oggetto "tmp" stesso potrebbe ancora essere allocata. Questo perché l'interfaccia IDisposable non ha assolutamente alcuna connessione con il garbage collector. Se si desidera assicurarsi che la memoria sia stata liberata, l'unica opzione sarebbe chiamare GC.Collect() per forzare una garbage collection.

Tuttavia, non si può sottolineare abbastanza che probabilmente non è una buona idea. In genere è molto meglio lasciare che il garbage collector faccia ciò che è stato progettato per fare, cioè gestire la memoria.

Cosa succede se la risorsa viene utilizzata per un periodo di tempo più lungo, in modo tale che la sua durata di vita superi diversi metodi? Chiaramente, l'istruzione "using" non è più applicabile, quindi il programmatore dovrebbe chiamare manualmente "Dispose" quando ha terminato la risorsa. E cosa succede se il programmatore dimentica? Se non vi è alcun ripiego, il processo o il computer potrebbe esaurire la risorsa che non viene correttamente liberata.

Ecco dove arrivano i finalizzatori. Un finalizzatore è un metodo sulla tua classe che ha una relazione speciale con il garbage collector. Il GC promette che - prima di liberare la memoria per qualsiasi oggetto di quel tipo - prima darà al finalizzatore la possibilità di fare una sorta di pulizia.

Quindi, nel caso di un file, teoricamente non è necessario chiudere il file manualmente. Possiamo solo aspettare che il garbage collector arrivi e lasciare che il finalizzatore faccia il lavoro. Sfortunatamente, ciò non funziona in pratica perché il garbage collector funziona in modo non deterministico. Il file potrebbe rimanere aperto molto più a lungo di quanto il programmatore si aspetti. E se vengono mantenuti aperti abbastanza file, il sistema potrebbe non riuscire quando si tenta di aprire un file aggiuntivo.

Per la maggior parte delle risorse, vogliamo entrambe queste cose. Vogliamo che una convention sia in grado di dire "abbiamo finito con questa risorsa ora" e vogliamo assicurarci che ci siano almeno alcune possibilità che la pulizia avvenga automaticamente se ci dimentichiamo di farlo manualmente. È qui che entra in gioco il modello "IDisposable". Questa è una convenzione che consente a IDispose e un finalizzatore di giocare bene insieme. Puoi vedere come funziona il modello guardando la documentazione ufficiale per IDisposable .

In conclusione : se quello che vuoi veramente fare è semplicemente assicurarti che la memoria sia liberata, quindi IDisposable e finalizzatori non ti aiuteranno. Ma l'interfaccia IDisposable fa parte di un modello estremamente importante che tutti i programmatori .NET dovrebbero comprendere.


In risposta alla domanda originale, con le informazioni fornite finora dal poster originale, è certo al 100% che non ne sappia abbastanza sulla programmazione in .NET per ricevere una risposta: utilizzare GC.Collect (). Direi che è del 99,99% probabilmente che non ha davvero bisogno di usare GC.Collect () affatto, come ha sottolineato la maggior parte dei posters.

La risposta corretta si riduce a "Lascia che il GC faccia il suo lavoro. Periodo. Hai altre cose di cui preoccuparti. Ma potresti decidere se e quando smaltire o ripulire oggetti specifici e se è necessario implementare IDisposable e possibilmente Finalize nella tua classe. "

Per quanto riguarda il post di Keith e la sua regola n. 4:

Alcuni poster confondono la regola 3 e la regola 4. La regola 4 di Keith è assolutamente corretta, inequivocabilmente. È l'unica regola dei quattro che non richiede alcuna modifica. Vorrei riformulare leggermente alcune delle sue altre regole per renderle più chiare, ma sono sostanzialmente corrette se le analizzi correttamente e in realtà leggi l'intero post per vedere come si espande su di loro.

  1. Se la tua classe non usa una risorsa non gestita E inoltre non crea mai un'istanza di un altro oggetto di una classe che utilizza, direttamente o alla fine, un oggetto non gestito (cioè una classe che implementa IDisposable), quindi non ci sarebbe bisogno della tua classe sia implementare IDisposable stesso, o anche chiamare .dispose su qualsiasi cosa. (In tal caso, è sciocco pensare che sia effettivamente NECESSARIO liberare immediatamente la memoria con un GC forzato, comunque).

  2. Se la tua classe utilizza una risorsa non gestita, OR crea un'istanza di un altro oggetto che implementa IDisposable, allora la tua classe dovrebbe:

    a) dispose / rilascialo immediatamente in un contesto locale in cui sono stati creati, OPPURE ...

    b) implementare IDisposable nel modello raccomandato all'interno del post di Keith, o qualche migliaio di posti su internet, o in letteralmente circa 300 libri ormai.

    b.1) Inoltre, se (b), ed è una risorsa non gestita che è stata aperta, entrambi IDisposable AND Finalize DEVONO SEMPRE essere implementati, secondo la regola # 4 di Keith.
    In questo contesto, Finalize è assolutamente una rete di sicurezza in un certo senso: se qualcuno crea un'istanza dell'oggetto IDisposable che utilizza una risorsa non gestita e non riescono a chiamare dispose, Finalize è l'ultima possibilità per il tuo oggetto di chiudere correttamente la risorsa non gestita.
    (Finalize dovrebbe farlo chiamando Dispose in modo tale che il metodo Dispose salti oltre il rilascio di qualcosa MA la risorsa non gestita. In alternativa, se il metodo Dispose dell'oggetto è chiamato correttamente da qualsiasi oggetto istanziato, allora ENTRAMBI passa la chiamata Dispose a tutti gli oggetti idisposabili che ha istanziato, E rilascia correttamente le risorse non gestite, terminando con una chiamata per sopprimere Finalize sul proprio oggetto, il che significa che l'impatto dell'utilizzo di Finalize si riduce se l'oggetto è disposto correttamente dal chiamante. sono inclusi nel post di Keith, BTW.)

    b.2) SE la tua classe sta implementando solo IDisposable perché deve essenzialmente passare su Dispose a un oggetto IDisposable che ha istanziato, quindi non implementare un metodo Finalize nella tua classe in quel caso. Finalizza è per la gestione del caso che BOTH Dispose non è mai stato chiamato da qualsiasi istanza del tuo oggetto, E una risorsa non gestita è stata utilizzata che è ancora inedita.

In breve, per quanto riguarda il post di Keith, è completamente corretto, e quel post è la risposta più corretta e completa, secondo me. Può usare alcune affermazioni a mano breve che alcuni trovano "sbagliato" o oggetto, ma il suo post completo si espande completamente sull'uso di Finalize, ed è assolutamente corretto. Assicurati di leggere il suo post completamente prima di saltare su una delle regole o dichiarazioni preliminari nel suo post.


L'interfaccia IDisposable è in realtà per le classi che contengono risorse non gestite. Se la tua classe non contiene risorse non gestite, perché è necessario liberare risorse prima che il garbage collector esegua? Altrimenti, assicurati solo che il tuo oggetto sia istanziato il più tardi possibile e che vada fuori campo il prima possibile.


Le risposte a questa domanda sono più che un po 'confuse.

Il titolo chiede informazioni sullo smaltimento, ma poi dice che vogliono tornare immediatamente alla memoria.

.Net è gestito , il che significa che quando scrivi app .Net non ti devi preoccupare direttamente della memoria, il costo è che non hai nemmeno il controllo diretto sulla memoria.

.Net decide quando è meglio ripulire e liberare memoria, non come coder .Net.

Il Dispose è un modo per dire. Net che hai finito con qualcosa, ma in realtà non libererà la memoria finché non sarà il momento migliore per farlo.

Fondamentalmente. Net raccoglierà effettivamente la memoria quando è più facile farlo - è molto bravo a decidere quando. A meno che tu non stia scrivendo qualcosa che richiede molta memoria, di solito non hai bisogno di sovrascriverlo (questo è parte del motivo per cui i giochi non sono spesso scritti in .Net ancora - hanno bisogno di un controllo completo)

In .Net puoi usare GC.Collect() per forzarlo immediatamente, ma è quasi sempre una cattiva pratica. Se .Net non lo ha ancora pulito, ciò significa che non è un momento particolarmente favorevole per farlo.

GC.Collect() preleva gli oggetti che .Net identifica come fatto con. Se non hai eliminato un oggetto che ne ha bisogno. Net può decidere di mantenere quell'oggetto. Ciò significa che GC.Collect() è efficace solo se implementi correttamente le istanze disponibili.

GC.Collect() non sostituisce correttamente l'utilizzo di IDisposable.

Quindi, il deposito e la memoria non sono direttamente correlati, ma non è necessario che lo siano. Lo smaltimento corretto renderà le tue app .Net più efficienti e quindi userà meno memoria.

Il 99% delle volte in .Net la seguente è la migliore pratica:

Regola 1: se non gestisci nulla non gestito o che implementa IDisposable , non preoccuparti di Dispose.

Regola 2: se si dispone di una variabile locale che implementa IDisposable, assicurarsi di eliminarla nell'ambito corrente:

//using is best practice
using( SqlConnection con = new SqlConnection("my con str" ) )
{
    //do stuff
} 

//this is what 'using' actually compiles to:
SqlConnection con = new SqlConnection("my con str" ) ;
try
{
    //do stuff
}
finally
{
    con.Dispose();
}

Regola 3: se una classe ha una proprietà o una variabile membro che implementa IDisposable, allora quella classe dovrebbe implementare anche IDisposable. Nel metodo Dispose di quella classe puoi anche disporre delle tue proprietà IDisposable:

//rather basic example
public sealed MyClass :
   IDisposable
{   
    //this connection is disposable
    public SqlConnection MyConnection { get; set; }

    //make sure this gets rid of it too
    public Dispose() 
    {
        //if we still have a connection dispose it
        if( MyConnection != null )
            MyConnection.Dispose();

        //note that the connection might have already been disposed
        //always write disposals so that they can be called again
    }
}

Questo non è veramente completo, ed è per questo che l'esempio è sigillato. Le classi ereditarie potrebbero dover osservare la regola successiva ...

Regola 4: se una classe utilizza una risorsa non gestita , implementa IDispose e aggiungi un finalizzatore.

.Net non può fare nulla con la risorsa non gestita , quindi ora stiamo parlando di memoria. Se non lo si pulisce, si può verificare una perdita di memoria.

Il metodo Dispose deve gestire sia le risorse gestite che quelle non gestite .

Il finalizzatore è un accorgimento sicuro: garantisce che se qualcun altro crea e un'istanza della classe e non riesce a disporne, le risorse non gestite "pericolose" possono ancora essere eliminate da .Net.

~MyClass()
{
    //calls a protected method 
    //the false tells this method
    //not to bother with managed
    //resources
    this.Dispose(false);
}

public void Dispose()
{
    //calls the same method
    //passed true to tell it to
    //clean up managed and unmanaged 
    this.Dispose(true);

    //as dispose has been correctly
    //called we don't need the 

    //'backup' finaliser
    GC.SuppressFinalize(this);
}

Finalmente questo sovraccarico di Dispose che prende una bandiera booleana:

protected virtual void Dispose(bool disposing)
{
    //check this hasn't been called already
    //remember that Dispose can be called again
    if (!disposed)
    {
        //this is passed true in the regular Dispose
        if (disposing)
        {
            // Dispose managed resources here.
        }

        //both regular Dispose and the finaliser
        //will hit this code
        // Dispose unmanaged resources here.
    }

    disposed = true;
}

Si noti che una volta che tutto ciò è a posto, un altro codice gestito che crea un'istanza della classe può semplicemente trattarlo come qualsiasi altro IDisposto (Regole 2 e 3).


Puoi avere distruzione di oggetti deterministici in c ++

Non si desidera chiamare GC.Collect, ma mette a disagio l'autoconfigurazione del garbage collector per rilevare la pressione della memoria e in alcuni casi non fa altro che aumentare la generazione corrente di ogni oggetto sull'heap.

Per coloro che inviano risposte IDisposable. Chiamare un metodo Dispose non distrugge un oggetto come descritto dal richiedente.


Sarebbe opportuno menzionare anche che lo smaltimento non si riferisce sempre alla memoria? Dispongo le risorse come riferimenti ai file più spesso della memoria. GC.Collect () si riferisce direttamente al garbage collector CLR e può o meno liberare memoria (in Task Manager). Probabilmente avrà un impatto negativo sulla tua applicazione (es. Performance).

Alla fine della giornata, perché vuoi tornare immediatamente alla memoria? Se c'è pressione di memoria da qualche altra parte, il sistema operativo ti darà la memoria nella maggior parte dei casi.


Se non vuoi (o non puoi) implementare IDisposable sulla tua classe, puoi forzare la garbage collection in questo modo (ma è lento) -

GC.Collect();

Spiacente ma la risposta selezionata qui non è corretta. Come poche persone hanno dichiarato successivamente Smaltire e implementare IDisposable non ha nulla a che fare con la liberazione della memoria associata a una classe .NET. È principalmente e tradizionalmente utilizzato per liberare risorse non gestite come handle di file, ecc.

Mentre la tua applicazione può chiamare GC.Collect () per provare a forzare una collezione da parte del garbage collector, ciò avrà davvero un effetto su quegli elementi che sono al livello di generazione corretto nella coda freachable. Quindi è possibile che se hai cancellato tutti i riferimenti all'oggetto potrebbero ancora essere un paio di chiamate a GC.Collect () prima che la memoria effettiva venga liberata.

Non dici nella tua domanda PERCHÉ senti il ​​bisogno di liberare immediatamente la memoria. Capisco che a volte ci possono essere circostanze insolite ma seriamente, nel codice gestito è quasi sempre meglio lasciare che il runtime gestisca la gestione della memoria.

Probabilmente il miglior consiglio se pensi che il tuo codice stia utilizzando la memoria più velocemente di quanto GC lo stia liberando, dovresti rivedere il tuo codice per assicurarti che nessun oggetto che non è più necessario sia referenziato in qualsiasi struttura di dati che hai nei membri statici ecc. Inoltre, cerca di evitare situazioni in cui hai riferimenti ad oggetti circolari poiché è possibile che anche questi non vengano liberati.


Questo articolo ha una procedura abbastanza semplice. Tuttavia, dover chiamare il GC invece di lasciarlo prendere il suo corso naturale è generalmente un segno di cattiva progettazione / gestione della memoria, soprattutto se non vengono consumate risorse limitate (connessioni, handle, qualsiasi altra cosa che in genere porta all'implementazione di IDisposable).

Cosa ti causa aver bisogno di fare questo?


public class MyClass : IDisposable
{
    public void Dispose()
    {
       // cleanup here
    }
}

allora puoi fare qualcosa di simile

MyClass todispose = new MyClass();
todispose.Dispose(); // instance is disposed right here

o

using (MyClass instance = new MyClass())
{

}
// instance will be disposed right here as it goes out of scope




dispose