Beste werkwijze voor het afdwingen van vuilnisophaal in C#

In mijn ervaring lijkt het erop dat de meeste mensen je zullen vertellen dat het onverstandig is om een ​​vuilnisophaaldienst af te dwingen, maar in sommige gevallen waar je werkt met grote objecten die niet altijd worden verzameld in de 0-generatie, maar waar het geheugen een probleem, is het ok om het verzamelen te forceren? Is er een best practice om dit te doen?


Antwoord 1, autoriteit 100%

Het beste is om een ​​ophaaldienst niet af te dwingen.

Volgens MSDN:

“Het is mogelijk om afval te forceren
ophalen door Collect te bellen, maar
meestal zou dit moeten zijn
vermeden omdat het kan leiden tot
prestatie problemen. “

Als u uw code echter betrouwbaar kunt testen om te bevestigen dat het aanroepen van Collect() geen negatief effect heeft, ga dan uw gang…

Probeer er gewoon voor te zorgen dat objecten worden opgeruimd als u ze niet meer nodig hebt. Als je aangepaste objecten hebt, kijk dan naar het gebruik van de “using statement” en de IDisposable-interface.

Deze link heeft een aantal goede praktische adviezen met betrekking tot het vrijmaken van geheugen / garbage collection etc:

http://msdn.microsoft.com/en-us/library/ 66x5fx1b.aspx


Antwoord 2, autoriteit 31%

Bekijk het op deze manier: is het efficiënter om het keukenafval weg te gooien als de vuilnisbak voor 10% is, of om het vol te laten lopen voordat je het buiten zet?

Door het niet vol te laten lopen, verspil je je tijd aan het lopen van en naar de vuilnisbak buiten. Dit is analoog aan wat er gebeurt wanneer de GC-thread wordt uitgevoerd – alle beheerde threads worden onderbroken terwijl deze wordt uitgevoerd. En als ik me niet vergis, kan de GC-thread worden gedeeld door meerdere AppDomains, dus het verzamelen van afval heeft invloed op alle domeinen.

Natuurlijk kun je een situatie tegenkomen waarin je op korte termijn niets aan de prullenbak hoeft toe te voegen, bijvoorbeeld als je op vakantie gaat. Dan is het een goed idee om het afval weg te gooien voordat je naar buiten gaat.

Dit kan een keer zijn dat het forceren van een GC kan helpen – als uw programma niet actief is, wordt het gebruikte geheugen niet verzameld omdat er geen toewijzingen zijn.


Antwoord 3, autoriteit 30%

Het beste is om in de meeste gevallen geen afvalophaling te forceren.(Elk systeem waaraan ik heb gewerkt met geforceerde afvalophalingen, had onderstrepende problemen die, indien opgelost, de noodzaak tot gedwongen ophaling zouden hebben weggenomen. afvalinzameling en versnelde het systeem enorm.)

Er zijn enkele gevallenwaarin umeer weet over geheugengebruik dan de vuilnisman. Dit is waarschijnlijk niet het geval in een toepassing met meerdere gebruikers of een service die op meer dan één verzoek tegelijk reageert.

Bij sommige batch-type verwerkingenweet u echter meer dan de GC. bijv. overweeg een toepassing die.

  • Krijgt een lijst met bestandsnamen op de opdrachtregel
  • Verwerkt een enkel bestand en schrijft het resultaat vervolgens weg naar een resultatenbestand.
  • Tijdens het verwerken van het bestand worden er veel onderling verbonden objecten gemaakt die niet kunnen worden verzameld totdat de verwerking van het bestand is voltooid (bijvoorbeeld een ontledingsboom)
  • Houdt niet veel status vast tussen de bestanden die het heeft verwerkt.

U misschienkunt u (na zorgvuldig) testen beweren dat u een volledige garbagecollection moet forceren nadat u elk bestand heeft verwerkt.

Een ander geval is een service die om de paar minuten wakker wordt om sommige items te verwerken, en die geen status behoudt terwijl deze slaapt. Dan kan het de moeite waard zijn om een ​​volledige verzameling te forceren net voor het slapengaan kande moeite waard zijn.

De enige keer dat ik zou overwegen om te forceren
een verzameling is wanneer ik dat veel weet
van het object is onlangs gemaakt
en zeer weinig objecten zijn momenteel
waarnaar wordt verwezen.

Ik zou liever een garbage collection API hebben als ik hem hints kan geven over dit soort dingen zonder zelf een GC te hoeven forceren.

Zie ook “Rico Mariani’s prestatietips


Antwoord 4, autoriteit 19%

Ik denk dat het voorbeeld gegeven door Rico Marianiwas goed: het kan passend zijn om een ​​GC te activeren als er een significante verandering is in de status van de applicatie. In een documenteditor kan het bijvoorbeeld OK zijn om een ​​GC te activeren wanneer een document wordt gesloten.


Antwoord 5, autoriteit 16%

Er zijn maar weinig algemene richtlijnen voor programmeren die absoluut zijn. De helft van de tijd, wanneer iemand zegt ‘je doet het verkeerd’, spuien ze gewoon een bepaald dogma. In C was het angst voor zaken als zelf-aanpassende code of threads, in GC-talen dwingt het de GC of verhindert het dat de GC draait.

Zoals het geval is met de meeste richtlijnen en goede vuistregels (en goede ontwerppraktijken), zijn er zeldzame gevallen waarin het zinvol is om rond de gevestigde norm te werken. U moet er wel heel zeker van zijn dat u de zaak begrijpt, dat uw zaak echt de afschaffing van de gangbare praktijk vereist en dat u de risico’s en bijwerkingen begrijpt die u kunt veroorzaken. Maar er zijn zulke gevallen.

Programmeerproblemen zijn zeer divers en vereisen een flexibele aanpak. Ik heb gevallen gezien waarin het zinvol is om GC te blokkeren in door afval verzamelde talen en op plaatsen waar het zinvol is om het te activeren in plaats van te wachten tot het van nature gebeurt. In 95% van de gevallen zou een van beide een teken zijn dat het probleem niet goed is aangepakt. Maar 1 op de 20 keer is er waarschijnlijk een geldig argument voor te maken.


Antwoord 6, autoriteit 10%

Ik heb geleerd om de vuilnisophaaldienst niet te slim af te zijn. Dat gezegd hebbende, blijf ik gewoon bij het gebruik van usingtrefwoord wanneer ik te maken heb met onbeheerde bronnen zoals bestands-I/O of databaseverbindingen.


Antwoord 7, autoriteit 9%

Eén geval dat ik onlangs tegenkwam waarbij handmatige aanroepen van GC.Collect()nodig waren, was bij het werken met grote C++-objecten die waren verpakt in kleine beheerde C++-objecten, die op hun beurt toegankelijk waren vanuit C#.

De vuilnisophaler is nooit gebeld omdat de hoeveelheid beheerd geheugen te verwaarlozen was, maar de hoeveelheid niet-beheerd geheugen die werd gebruikt, was enorm. Als ik handmatig Dispose()op de objecten aanroep, moet ik zelf bijhouden wanneer objecten niet langer nodig zijn, terwijl het aanroepen van GC.Collect()alle objecten opruimt die worden niet langer doorverwezen…..


Antwoord 8, autoriteit 8%

Ik weet niet zeker of het een goede gewoonte is, maar wanneer ik met grote hoeveelheden afbeeldingen in een lus werk (dwz veel afbeeldingen/afbeeldingen/bitmap-objecten maken en verwijderen), laat ik de GC.Collect regelmatig.

Ik meen ergens gelezen te hebben dat de GC alleen draait als het programma (meestal) inactief is, en niet midden in een intensieve lus, dus dat zou kunnen lijken op een gebied waar handmatige GC zinvol zou kunnen zijn.


Antwoord 9, autoriteit 6%

Ik denk dat je de best practice al hebt genoemd en dat is om het NIET te gebruiken tenzij het ECHT nodig is. Ik raad je ten zeerste aan om je code in meer detail te bekijken en eventueel profileringstools te gebruiken om deze vragen eerst te beantwoorden.

  1. Heeft u iets in uw code dat items met een groter bereik aangeeft dan nodig is
  2. Is het geheugengebruik echt te hoog
  3. Vergelijk de prestaties voor en na het gebruik van GC.Collect() om te zien of het echt helpt.

Antwoord 10, autoriteit 4%

Stel dat uw programma geen geheugenlekkage heeft, objecten zich ophopen en niet kunnen worden geGC-ed in Gen 0 omdat:
1) Er wordt al heel lang naar verwezen, dus ga naar Gen1 & gen2;
2) Het zijn grote objecten (>80K), dus ga naar LOH (Large Object Heap). En LOH comprimeert niet zoals in Gen0, Gen1 & Gen2.

Controleer de prestatiemeter van “.NET Memory” en u kunt zien dat het 1) probleem echt geen probleem is. Over het algemeen zal elke 10 Gen0 GC 1 Gen1 GC activeren en elke 10 Gen1 GC 1 Gen2 GC. Theoretisch, GC1 & GC2 kan nooit worden GC-ed als er geen druk op GC0 staat (als het gebruik van het programmageheugen echt bedraad is). Het overkomt mij nooit.

Voor probleem 2) kunt u de prestatiemeter van “.NET Memory” controleren om te controleren of LOH opgeblazen wordt. Als het echt een probleem voor je probleem is, kun je misschien een grote object-pool maken, zoals deze blog suggereert http://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx.


Antwoord 11, autoriteit 4%

Ik wil graag toevoegen dat:
Het aanroepen van GC.Collect() (+ WaitForPendingFinalizers()) is een deel van het verhaal.
Zoals terecht door anderen is vermeld, is GC.COllect() een niet-deterministische verzameling en wordt het overgelaten aan het oordeel van de GC zelf (CLR).
Zelfs als u een aanroep toevoegt aan WaitForPendingFinalizers, is deze mogelijk niet bepalend.
Neem de code van deze msdn linken voer de code uit met de objectlus-iteratie als 1 of 2. Je zult ontdekken wat niet-deterministisch betekent (stel een breekpunt in in de destructor van het object).
Precies, de destructor wordt niet aangeroepen als er slechts 1 (of 2) slepende objecten waren door Wait..().[Citation reqd.]

Als uw code te maken heeft met onbeheerde bronnen (bijv. externe bestandshandles), moet u destructors (of finalizers) implementeren.

Hier is een interessant voorbeeld:

Opmerking: als je het bovenstaande voorbeeld van MSDN al hebt geprobeerd, zal de volgende code de lucht zuiveren.

class Program
{    
    static void Main(string[] args)
        {
            SomePublisher publisher = new SomePublisher();
            for (int i = 0; i < 10; i++)
            {
                SomeSubscriber subscriber = new SomeSubscriber(publisher);
                subscriber = null;
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine(SomeSubscriber.Count.ToString());
            Console.ReadLine();
        }
    }
    public class SomePublisher
    {
        public event EventHandler SomeEvent;
    }
    public class SomeSubscriber
    {
        public static int Count;
        public SomeSubscriber(SomePublisher publisher)
        {
            publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
        }
        ~SomeSubscriber()
        {
            SomeSubscriber.Count++;
        }
        private void publisher_SomeEvent(object sender, EventArgs e)
        {
            // TODO: something
            string stub = "";
        }
    }

Ik stel voor, analyseer eerst wat de uitvoer zou kunnen zijn en voer het dan uit en lees dan de reden hieronder:

{De destructor wordt alleen impliciet aangeroepen als het programma is afgelopen. }
Om het object deterministisch op te schonen, moet men IDisposable implementeren en een expliciete aanroep doen naar Dispose(). Dat is de essentie! 🙂


Antwoord 12, autoriteit 3%

Grote objecten worden toegewezen op LOH (large object heap), niet op gen 0. Als je zegt dat ze niet worden verzameld met gen 0, heb je gelijk. Ik geloof dat ze alleen worden verzameld wanneer de volledige GC-cyclus (generaties 0, 1 en 2) plaatsvindt.

Dat gezegd hebbende, geloof ik dat aan de andere kant GC zich zal aanpassen en het geheugen agressiever zal verzamelen als je met grote objecten werkt en de geheugendruk toeneemt.

Het is moeilijk te zeggen of er wel of niet wordt ingezameld en onder welke omstandigheden. Ik deed GC.Collect() na het verwijderen van dialoogvensters/formulieren met tal van besturingselementen enz. (omdat tegen de tijd dat het formulier en zijn besturingselementen in gen 2 terechtkomen vanwege het maken van veel gevallen van bedrijfsobjecten/het laden van veel gegevens – nee grote objecten natuurlijk), maar merkte op de lange termijn eigenlijk geen positieve of negatieve effecten op.


Antwoord 13, autoriteit 2%

Nog één ding: het expliciet activeren van GC Collect kan de prestaties van uw programma NIET verbeteren. Het is heel goed mogelijk om het nog erger te maken.

De .NET GC is goed ontworpen en afgestemd op adaptief, wat betekent dat het de drempel van GC0 / 1/2 kan aanpassen aan de “gewoonte” van uw programma-geheugengebruik. Dus, het wordt na enige tijd aan uw programma aangepast. Zodra u expliciet op GC.Collect roept, worden de drempels gereset! En de .NET moet tijd doorbrengen om zich opnieuw aan te passen aan de “gewoonte” van uw programma.

Mijn suggestie is altijd vertrouwen .NET GC. Alle geheugenprobleemoppervlakken, controleer “.NET-geheugen” prestatieteller en diagnosticeren mijn eigen code.


14

Als u echter op betrouwbare wijze uw code kunt testen om te bevestigen dat het bellen () geen negatieve impact heeft, ga dan verder …

IMHO, dit is vergelijkbaar met het zeggen van “als u kunt bewijzen dat uw programma nooit in de toekomst geen bugs zal hebben, ga dan verder …”

In alle ernst, het forceren van de GC is nuttig voor debugging / testdoeleinden. Als je het gevoel hebt dat je het op een andere tijden nodig hebt, dan vergist je of je programma verkeerd is gebouwd. Hoe dan ook, de oplossing dwingt niet de GC …

Other episodes