Domanda

Anche se capisco le gravi implicazioni del giocare con questa funzione (o almeno è quello che penso), non riesco a capire perché sta diventando una di queste cose che i programmatori rispettabili non useranno mai, anche quelli che non " so anche a cosa serve.

Supponiamo che stia sviluppando un'applicazione in cui l'utilizzo della memoria varia notevolmente a seconda di ciò che l'utente sta facendo. Il ciclo di vita dell'applicazione può essere suddiviso in due fasi principali: modifica ed elaborazione in tempo reale. Durante la fase di modifica, supponiamo che vengano creati miliardi o addirittura trilioni di oggetti; alcuni piccoli e altri no, alcuni potrebbero avere finalizzatori e altri no, e supponiamo che la loro durata di vita vari da pochi millisecondi a lunghe ore. Successivamente, l'utente decide di passare alla fase in tempo reale. A questo punto, supponiamo che le prestazioni svolgano un ruolo fondamentale e che la minima alterazione del flusso del programma possa portare a conseguenze catastrofiche. La creazione di oggetti viene quindi ridotta al minimo possibile utilizzando pool di oggetti e simili, ma poi, il GC entra in modo imprevisto e lo getta via, e qualcuno muore.

La domanda: in questo caso, non sarebbe saggio chiamare GC.Collect () prima di entrare nella seconda fase?

Dopotutto, queste due fasi non si sovrappongono mai in tempo tra loro e tutte le ottimizzazioni e le statistiche che il GC avrebbe potuto raccogliere sarebbero di scarsa utilità qui ...

Nota: come alcuni di voi hanno sottolineato, .NET potrebbe non essere la migliore piattaforma per un'applicazione come questa, ma questo va oltre lo scopo di questa domanda. L'intento è chiarire se una chiamata GC.Collect () può migliorare il comportamento / le prestazioni complessive di un'applicazione o meno. Siamo tutti d'accordo sul fatto che le circostanze in cui faresti una cosa del genere sono estremamente rare ma, di nuovo, il GC cerca di indovinare e lo fa perfettamente bene per la maggior parte del tempo, ma si tratta ancora di indovinare.

Grazie.

È stato utile?

Soluzione

Dal blog di Rico ...

  

Regola n. 1

     

Non farlo.

     

Questo è davvero il più importante   regola. È giusto dirlo di più   gli usi di GC.Collect () sono una cattiva idea   e ne ho parlato in dettaglio   la pubblicazione originale quindi non ripeterò   tutto qui. Quindi passiamo a ...

     

Regola n. 2

     

Prendi in considerazione l'idea di chiamare GC.Collect () se alcuni   evento non ricorrente è appena accaduto   e questo evento è altamente probabile   hanno causato molti vecchi oggetti a   morire.

     

Un classico esempio di questo è se lo sei   scrivendo un'applicazione client e tu   visualizzare un grande e complicato   modulo a cui sono associati molti dati   con esso. Il tuo utente ha appena   interagito con questo modulo potenzialmente   creando alcuni oggetti di grandi dimensioni ... cose   come documenti XML o un DataSet di grandi dimensioni   o due. Quando il modulo li chiude   gli oggetti sono morti e quindi GC.Collect ()   recupererà la memoria associata   con loro ...

Quindi sembra che questa situazione possa rientrare nella Regola n. 2, sai che c'è un momento nel tempo in cui molti vecchi oggetti sono morti e non è ricorrente. Tuttavia, non dimenticare le parole di separazione di Rico.

  

La regola n. 1 dovrebbe superare la regola n. 2 senza   prove concrete.

Misura, misura, misura.

Altri suggerimenti

Se si chiama GC.Collect () nel codice di produzione, si dichiara essenzialmente di conoscere più degli autori del GC. Questo potrebbe essere il caso. Tuttavia, di solito non lo è, e quindi fortemente scoraggiato.

Che ne dici di quando usi oggetti COM come MS Word o MS Excel da .NET? Senza chiamare GC.Collect dopo aver rilasciato gli oggetti COM, abbiamo scoperto che esistono ancora le istanze dell'applicazione Word o Excel.

In effetti il ??codice che usiamo è:

 
Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

Quindi sarebbe un uso errato del Garbage Collector? In tal caso, come possiamo far morire gli oggetti Interop? Inoltre, se non è pensato per essere usato in questo modo, perché il metodo GC del Collect è anche Public ?

Bene, il GC è una di quelle cose con cui ho una relazione amore / odio. Abbiamo lo ha rotto in passato tramite VistaDB e ha scritto sul suo blog. L'hanno risolto, ma ci vuole molto tempo per ottenere correzioni da loro su cose come questa.

Il GC è complesso e un approccio a una dimensione è molto, molto difficile da realizzare su qualcosa di così grande. La SM ha fatto un buon lavoro, ma a volte è possibile ingannare il GC.

In generale non dovresti aggiungere un Collect a meno che tu non sappia per certo che hai appena scaricato una tonnellata di memoria e andrà in crisi di mezza età se il GC non lo ripulisce ora.

Puoi rovinare l'intera macchina con una serie di istruzioni GC.Collect non valide. La necessità di un'istruzione di raccolta indica quasi sempre un errore di fondo più ampio. La perdita di memoria di solito ha a che fare con riferimenti e una mancanza di comprensione di come funzionano. O usando IDisposable su oggetti che non ne hanno bisogno e caricando molto di più il GC.

Osserva attentamente la percentuale di tempo trascorso in GC attraverso i contatori delle prestazioni del sistema. Se vedi l'app che utilizza il 20% o più del suo tempo nel GC hai seri problemi di gestione degli oggetti (o un modello di utilizzo anormale). Vuoi ridurre al minimo il tempo impiegato dal GC perché accelererà l'intera app.

È anche importante notare che il GC è diverso sui server rispetto alle workstation. Ho visto una serie di piccoli problemi difficili da rintracciare con le persone che non li testano entrambi (o nemmeno sono consapevoli del fatto che sono due).

E solo per essere il più completo possibile nella mia risposta, dovresti anche testare in Mono se stai prendendo di mira anche quella piattaforma. Poiché si tratta di un'implementazione completamente diversa, potrebbero verificarsi problemi totalmente diversi rispetto all'implementazione degli Stati membri.

Ci sono situazioni in cui è utile, ma in generale dovrebbe essere evitato. Potresti confrontarlo con GOTO o guidare un ciclomotore: lo fai quando è necessario, ma non lo dici ai tuoi amici.

Dalla mia esperienza non è mai stato consigliabile effettuare una chiamata a GC.Collect () nel codice di produzione. Nel debug, sì, ha i suoi vantaggi per aiutare a chiarire potenziali perdite di memoria. Immagino che la mia ragione fondamentale sia che il GC è stato scritto e ottimizzato da programmatori molto più intelligenti di me, e se arrivo a un punto in cui sento di dover chiamare GC.Collect () è un indizio che sono andato fuori strada da qualche parte. Nella tua situazione non sembra che tu abbia effettivamente problemi di memoria, solo che sei preoccupato per l'instabilità che la raccolta porterà al tuo processo. Visto che non ripulirà gli oggetti ancora in uso e che si adatta molto rapidamente sia alle richieste crescenti che a quelle più basse, penso che non dovrai preoccupartene.

Uno dei maggiori motivi per chiamare GC.Collect () è quando hai appena eseguito un evento significativo che crea molta spazzatura, come quello che descrivi. Chiamare GC.Collect () può essere una buona idea qui; in caso contrario, il GC potrebbe non capire che si è trattato di un evento "una tantum".

Naturalmente, dovresti profilarlo e vedere di persona.

Beh, ovviamente non dovresti scrivere codice con requisiti in tempo reale in lingue con garbage collection non in tempo reale.

In un caso con fasi ben definite, non vi sono problemi con l'attivazione del Garbage Collector. Ma questo caso è estremamente raro. Il problema è che molti sviluppatori cercheranno di usarlo per problemi di paper-over in uno stile di culto, e l'aggiunta indiscriminatamente causerà problemi di prestazioni.

La chiamata a GC.Collect () impone al CLR di eseguire uno stack walk per vedere se ogni oggetto può essere realmente rilasciato controllando i riferimenti. Ciò influirà sulla scalabilità se il numero di oggetti è elevato ed è stato anche noto che attiva la garbage collection troppo spesso. Fidati del CLR e lascia che il garbage collector si esegua da solo quando è appropriato.

Creazione di immagini in un ciclo - anche se si chiama dispose, la memoria non viene recuperata. La spazzatura si raccoglie ogni volta. Sono passato da 1,7 GB di memoria sulla mia app di elaborazione foto a 24 MB e le prestazioni sono eccellenti.

È assolutamente necessario chiamare GC.Collect.

Infatti, non credo sia una brutta pratica chiamare GC.Collect.
Potrebbero esserci casi in cui ne abbiamo bisogno. Ad esempio, ho un modulo che esegue un thread, che a sua volta apre tabelle diverse in un database, estrae il contenuto in un campo BLOB in un file temporaneo, crittografa il file, quindi legge il file in un binarystream e torna in un BLOB campo in un'altra tabella.

L'intera operazione richiede molta memoria e non è certa del numero di righe e delle dimensioni del contenuto del file nelle tabelle.
Avevo spesso l'eccezione OutofMemory e ho pensato che sarebbe saggio eseguire periodicamente GC.Collect sulla base di una variabile contatore. Incremento un contatore e quando viene raggiunto un livello specificato, viene chiamato GC per raccogliere eventuali rifiuti che potrebbero essersi formati e per recuperare la memoria persa a causa di perdite di memoria impreviste.

Dopo questo, penso che funzioni bene, almeno nessuna eccezione !!!
Chiamo nel modo seguente:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).

In .net, il tempo necessario per eseguire una garbage collection è molto più fortemente correlato alla quantità di cose che non sono spazzatura, piuttosto che alla quantità di cose che sono. Infatti, a meno che un oggetto non ignori Finalize (esplicitamente o tramite C # distruttore), è il bersaglio di un WeakReference , si trova sull'heap di oggetti di grandi dimensioni o è speciale in alcuni altro modo correlato a gc, l'unica cosa che identifica la memoria in cui si trova come un oggetto è l'esistenza di riferimenti radicati ad esso. Altrimenti, l'operazione del GC è analoga a prendere da un edificio tutto di valore e dinamizzare l'edificio, costruirne uno nuovo sul sito di quello vecchio e inserirvi tutti gli oggetti di valore. Lo sforzo richiesto per dinamite l'edificio è totalmente indipendente dalla quantità di immondizia al suo interno.

Di conseguenza, chiamare GC.Collect può aumentare la quantità complessiva di lavoro che il sistema deve svolgere. Ritarderà il verificarsi della raccolta successiva, ma probabilmente farà immediatamente altrettanto lavoro quanto la raccolta successiva avrebbe richiesto quando si è verificata; nel momento in cui si sarebbe verificata la prossima raccolta, il tempo totale impiegato per la raccolta sarebbe stato più o meno lo stesso di cui non era stato chiamato GC.Collect , ma il sistema avrebbe accumulato un po 'di rifiuti, causando la raccolta successiva sarà richiesta prima di quanto non fosse stato chiamato GC.Collect .

Le volte in cui riesco a vedere GC.Collect davvero utili sono quando si deve misurare l'utilizzo della memoria di un codice (poiché le cifre sull'utilizzo della memoria sono veramente significative solo dopo una raccolta) o profilo quale dei diversi algoritmi è migliore (chiamare GC.Collect () prima di eseguire ciascuno dei vari pezzi di codice può aiutare a garantire uno stato di base coerente). Ci sono alcuni altri casi in cui uno potrebbe sapere cose che il GC non conosce, ma a meno che non si stia scrivendo un programma a thread singolo, non c'è modo di sapere che una chiamata GC.Collect potrebbe aiutare le strutture di dati di un thread evitano "crisi di mezza età" non farebbe sì che i dati di altri thread abbiano una "crisi di mezza età" che altrimenti sarebbe stato evitato.

Abbiamo avuto un problema simile con il Garbage Collector che non raccoglieva rifiuti e liberava memoria.

Nel nostro programma, stavamo elaborando alcuni fogli di calcolo Excel di dimensioni modeste con OpenXML. I fogli di calcolo contenevano ovunque da 5 a 10 "fogli". con circa 1000 righe di 14 colonne.

Il programma in un ambiente a 32 bit (x86) si arresterebbe in modo anomalo con una memoria "esaurita" errore. L'abbiamo fatto funzionare in un ambiente x64, ma volevamo una soluzione migliore.

Ne abbiamo trovato uno.

Ecco alcuni frammenti di codice semplificati di ciò che non ha funzionato e che cosa ha funzionato quando si trattava di chiamare esplicitamente Garbage Collector per liberare memoria dagli oggetti disposti.

La chiamata al GC dall'interno della subroutine non ha funzionato. La memoria non è mai stata recuperata ...

 
For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

Spostando la chiamata GC all'esterno dell'ambito della subroutine, la spazzatura è stata raccolta e la memoria è stata liberata.

 
For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

Spero che questo aiuti gli altri che sono frustrati dalla raccolta dati inutili di .NET quando sembra ignorare le chiamate a GC.Collect () .

Paul Smith

Non c'è niente di sbagliato nel chiamare esplicitamente una raccolta. Alcune persone vogliono davvero credere che se si tratta di un servizio fornito dal venditore, non metterlo in discussione. Oh, e tutti quei blocchi casuali nei momenti sbagliati della tua applicazione interattiva? La prossima versione lo migliorerà!

Lasciare che un processo in background gestisca la manipolazione della memoria significa non doverlo gestire da soli, vero. Ma ciò non significa logicamente che per noi sia meglio non affrontarlo da soli in tutte le circostanze. Il GC è ottimizzato per la maggior parte dei casi. Ma ciò non significa logicamente che sia ottimizzato in tutti i casi.

Hai mai risposto a una domanda aperta come "qual è il miglior algoritmo di ordinamento" con una risposta definitiva? In tal caso, non toccare il GC. Per quelli di voi che hanno chiesto le condizioni o hanno dato risposte di tipo "in questo caso", è possibile procedere a conoscere il GC e quando attivarlo.

Devo dire che ho avuto blocchi di applicazioni in Chrome e Firefox che mi frustrano a morte, e anche in alcuni casi la memoria cresce senza ostacoli - Se solo imparassero a chiamare il garbage collector - oppure mi ha dato un pulsante in modo che quando comincio a leggere il testo di una pagina posso colpirlo e quindi essere libero da blocchi per i prossimi 20 minuti.

Penso che tu abbia ragione sullo scenario, ma non sono sicuro dell'API.

Microsoft afferma che in questi casi è necessario aggiungere pressione sulla memoria come suggerimento per il CG che dovrebbe presto eseguire una raccolta.

Cosa c'è che non va? Il fatto che tu stia indovinando il garbage collector e l'allocatore di memoria, che tra loro hanno un'idea molto più grande sull'utilizzo effettivo della memoria della tua applicazione in fase di esecuzione rispetto a te.

Il desiderio di chiamare GC.Collect () di solito sta cercando di nascondere gli errori che hai fatto altrove!

Sarebbe meglio se scopri dove hai dimenticato di smaltire le cose che non ti servivano più.

In conclusione, puoi profilare l'applicazione e vedere come queste raccolte aggiuntive influenzano le cose. Suggerirei di starne alla larga, a meno che tu non abbia intenzione di fare il profilo. Il GC è progettato per prendersi cura di se stesso e man mano che il tempo di esecuzione si evolve, possono aumentare l'efficienza. Non vuoi un mucchio di codice in giro che potrebbe confondere le opere e non essere in grado di trarre vantaggio da questi miglioramenti. Esiste un argomento simile per usare foreach invece che per for che, in questo caso, è possibile aggiungere a foreach futuri miglioramenti sotto le copertine e il codice non deve cambiare per trarne vantaggio.

Lo stesso .NET Framework non è mai stato progettato per essere eseguito in un ambiente in tempo reale. Se hai davvero bisogno di un'elaborazione in tempo reale, utilizzeresti un linguaggio in tempo reale incorporato che non è basato su .NET o utilizzi .NET Compact Framework in esecuzione su un dispositivo Windows CE.

Il peggio che farà sarà bloccare il programma per un po '. Quindi, se per te va bene, fallo. Di solito non è necessario per client spessi o app Web con l'interazione principalmente dell'utente.

Ho scoperto che a volte i programmi con thread a esecuzione prolungata, o programmi batch, ottengono l'eccezione OutOfMemory anche se stanno disponendo correttamente gli oggetti. Ricordo che era una elaborazione di transazioni di database line-of-business; l'altro era una routine di indicizzazione su un thread in background in un'app client spessa.

In entrambi i casi, il risultato è stato semplice: No GC.Collect, memoria insufficiente, coerente; GC.Collect, prestazioni impeccabili.

L'ho provato diverse volte, per risolvere i problemi di memoria, senza risultati. L'ho tolto.

In breve, non inserirlo a meno che non si verifichino errori. Se lo inserisci e non risolve il problema di memoria, rimuovilo. Ricorda di provare in modalità Rilascio e confronta le mele con le mele.

L'unica volta in cui le cose possono andare storte è quando si diventa moralisti al riguardo. Non è un problema di valori; molti programmatori sono morti e sono andati dritti in paradiso con molti GC inutili. Raccolte nel loro codice, che li sopravvive.

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