Domanda

Sto scrivendo un'applicazione finanziaria C# che riceve messaggi dalla rete, li traduce in oggetti diversi a seconda del tipo di messaggio e infine applica su di essi la logica aziendale dell'applicazione.

Il punto è che, una volta applicata la logica aziendale, sono molto sicuro che non avrò mai più bisogno di questa istanza.Piuttosto che aspettare che il Garbage Collector li liberi, vorrei "eliminarli" esplicitamente.

Esiste un modo migliore per farlo in C#, dovrei utilizzare un pool di oggetti per riutilizzare sempre lo stesso set di istanze o esiste una strategia migliore.

L'obiettivo è evitare che la garbage collection utilizzi qualsiasi CPU durante un processo critico in termini di tempo.

È stato utile?

Soluzione

Non eliminarli subito.Chiamare il garbage collector per ogni oggetto è una cattiva idea.Normalmente tu Veramente non voglio assolutamente scherzare con il garbage collector, e anche i processi critici in termini di tempo sono solo condizioni di gara in attesa di verificarsi se sono così sensibili.

Ma se sai che avrai periodi di carico intenso o di carico leggero per la tua app, potresti provare un GC.Collect() più generale quando raggiungi un periodo di carico leggero per incoraggiare la pulizia prima del periodo di carico successivo.

Altri suggerimenti

Guarda qui: http://msdn.microsoft.com/en-us/library/bb384202.aspx

Puoi dire al netturbino che stai facendo qualcosa di importante in questo momento e lui cercherà di essere gentile con te.

Colpisci dentro te stesso: usa un pool di oggetti e riutilizza quegli oggetti.La semantica delle chiamate a quegli oggetti dovrebbe essere nascosta dietro la facciata di una fabbrica.Dovrai far crescere il pool in un modo predefinito.Forse raddoppiare la dimensione ogni volta che raggiunge il limite: un algoritmo di livello alto o una percentuale fissa.Ti consiglio vivamente di non chiamare GC.Collect().

Quando il carico sul tuo pool diventa sufficientemente basso, potresti ridurre il pool e ciò alla fine attiverà una raccolta dei rifiuti: lascia che sia il CLR a preoccuparsene.

Tentare di indovinare il garbage collector è generalmente una pessima idea.Su Windows, il garbage collector lo è uno generazionale e si può fare affidamento sul fatto che sia piuttosto efficiente.Esistono alcune eccezioni a questa regola generale: la più comune è il verificarsi di un evento occasionale che sai per certo avrà causato la morte di molti vecchi oggetti, una volta che gli oggetti sono stati promossi a Gen2 (il più longevo) tendono a restare in giro.

Nel caso che menzioni, sembra che tu stia generando una serie di oggetti di breve durata: questi si tradurranno in raccolte Gen0.Questi accadono comunque relativamente spesso e sono i più efficienti.Potresti evitarli avendo un pool riutilizzabile di oggetti, se preferisci, ma è meglio accertarsi con certezza se GC è un problema di prestazioni prima di intraprendere tale azione: il profiler CLR è lo strumento per farlo.

Va notato che il garbage collector è diverso su diversi framework .NET: sul framework compatto (che funziona su Xbox 360 e su piattaforme mobili) è un GC non generazionale e come tale devi stare molto più attento a cosa spazzatura generata dal tuo programma.

Forzare un GC.Collect() è generalmente una cattiva idea, lascia che GC faccia ciò che sa fare meglio.Sembra che la soluzione migliore sia quella di utilizzare un pool di oggetti che puoi espandere se necessario: ho utilizzato questo modello con successo.

In questo modo eviti non solo la raccolta dei rifiuti ma anche i normali costi di allocazione.

Infine, sei sicuro che il GC ti stia causando un problema?Probabilmente dovresti misurarlo e dimostrarlo prima di implementare qualsiasi soluzione che riduca le prestazioni: potresti causare lavoro inutile!

"L'obiettivo è evitare che la garbage collection utilizzi qualsiasi CPU durante un processo critico in termini di tempo"

Q: Se per tempo critico intendi che stai ascoltando un pezzo di hardware esoterico e non puoi permetterti di perdere l'interruzione?

UN: Se è così, allora C# non è il linguaggio da usare, per quello vuoi Assembler, C o C++.

Q: Se per tempo critico intendi mentre ci sono molti messaggi nella pipe e non vuoi lasciare che il Garbage Collector rallenti le cose?

UN: Se è così ti stai preoccupando inutilmente.A quanto pare i tuoi oggetti hanno vita molto breve, questo significa che il garbage collector li riciclerà in modo molto efficiente, senza alcun apparente ritardo nelle prestazioni.

Tuttavia, l'unico modo per saperlo con certezza è testarlo, impostarlo per l'esecuzione durante la notte elaborando un flusso costante di messaggi di prova, rimarrò sbalordito se le tue statistiche sulle prestazioni riescono a individuare quando entra in funzione il GC (e anche se puoi individuatelo, sarei ancora più sorpreso se fosse davvero importante).

Ottieni una buona comprensione e senti come si comporta il Garbage Collector e capirai perché ciò a cui stai pensando qui non è raccomandato.a meno che non ti piaccia davvero che CLR trascorra del tempo a riorganizzare gli oggetti in memoria molto.

Quanto è impegnativa l'app?Ho scritto un'app che cattura 3 schede audio (DirectX gestita, 44,1 KHz, Stereo, 16 bit), in blocchi da 8 KB e invia 2 dei 3 flussi a un altro computer tramite TCP/IP.L'interfaccia utente visualizza un misuratore del livello audio e un titolo/artista a scorrimento (fluido) per ciascuno dei 3 canali.Funziona su PC con XP, 1,8 GHz, 512 MB, ecc.L'App utilizza circa il 5% della CPU.

Ho evitato di richiamare manualmente i metodi GC.Ma ho dovuto mettere a punto alcune cose che erano inutili.Ho usato il profiler Ant di RedGate per affinare le porzioni sprecate.Uno strumento fantastico!

Volevo utilizzare un pool di array di byte pre-allocati, ma l'assembly DX gestito alloca internamente i buffer di byte, quindi li restituisce all'app.Si è scoperto che non dovevo farlo.

Se il tempo è assolutamente critico, dovresti utilizzare una piattaforma deterministica come C/C++.Anche la chiamata a GC.Collect() genererà cicli della CPU.

La tua domanda inizia con il suggerimento di voler risparmiare memoria ma eliminare gli oggetti.Questa è un'ottimizzazione critica dello spazio.Devi decidere cosa vuoi veramente perché il GC è più bravo di un essere umano nell'ottimizzare questa situazione.

A quanto pare, sembra che tu stia parlando di finalizzazione deterministica (distruttori in C++), che non esiste in C#.La cosa più vicina che troverai in C# è il modello Disposable.Fondamentalmente implementi il ​​file Monouso interfaccia.

Lo schema base è questo:

public class MyClass: IDisposable
{
    private bool _disposed;

    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected virtual void Dispose( bool disposing )
    {
        if( _disposed )    
            return;

        if( disposing )
        {
            // Dispose managed resources here
        }

        _disposed = true;
    }
}

Potresti avere un numero limitato di istanze di ogni tipo in un pool e riutilizzare quanto già fatto con le istanze.La dimensione del pool dipende dalla quantità di messaggi che elaborerai.

Invece di creare una nuova istanza di un oggetto ogni volta che ricevi un messaggio, perché non riutilizzi oggetti che sono già stati utilizzati?In questo modo non dovrai combattere contro il garbage collector e la tua memoria heap non verrà frammentata.**

Per ogni tipo di messaggio, puoi creare un pool per contenere le istanze che non sono in uso.Ogni volta che ricevi un messaggio di rete, esamini il tipo di messaggio, estrai un'istanza in attesa dal pool appropriato e applichi la logica aziendale.Successivamente, reinserisci l'istanza dell'oggetto messaggio nel suo pool.

Molto probabilmente vorrai eseguire il "caricamento lento" del tuo pool con istanze in modo che il tuo codice si ridimensioni facilmente.Pertanto, la classe del pool dovrà rilevare quando è stata estratta un'istanza nulla e riempirla prima di distribuirla.Quindi, quando il codice chiamante lo rimette nel pool, è un'istanza reale.

** "Il pool di oggetti è un modello da utilizzare che consente di riutilizzare gli oggetti anziché allocarli e deallocarli, il che aiuta a prevenire la frammentazione dell'heap e costose compattazioni GC."

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pooling-and.aspx

In teoria il GC non dovrebbe funzionare se la CPU è sotto carico pesante o a meno che non sia realmente necessario.Ma se necessario, potresti semplicemente voler mantenere tutti i tuoi oggetti in memoria, forse un'istanza singleton, e non ripulirli mai a meno che tu non sia pronto.Questo è probabilmente l'unico modo per garantire quando il GC viene eseguito.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top