7 thoughts on “Three Garbage Examples

    1. Hi there, and good question! I used a combination of two things to measure/diagnose.

      Firstly, I used a profiler (in particular I used YourKit -> https://www.yourkit.com/.net/profiler/features/) to diagnose areas that were creating a lot of garbage in my game engine. As well as some more bespoke/esoteric cases unique to my own software, the three examples I gave in this post are ones that cropped up frequently in my profiling.

      To get actual numbers though I wrote a very simplistic benchmark harness and ran the code above through it. That’s the reason for the giant disclaimer in the ‘final remarks’ section- the benchmark is susceptible to all sorts of ‘outside’ interference and shouldn’t really be used for anything other than comparing like-for-like code.

      I won’t copy the entirety of the benchmark code here because it’s a bit lengthy (it goes through multiple iterations including some dummy runs to give the JIT time to ‘warm up’ as well as recording elapsed time, getting averages, etc), but the most pertinent part (abridged) is as follows:

      GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
      if (!GC.TryStartNoGCRegion(MAX_BYTES_PER_TEST)) throw new ApplicationException("Could not record GC.");
      DoTest();
      long memBefore = GC.GetTotalMemory(false);
      GC.EndNoGCRegion();
      long garbageBytes = Math.Max(0L, memBefore - GC.GetTotalMemory(true));
      

      Just to give some examples of ways in which this code is inaccurate: The GC can still run between starting/stopping the no-gc region and the lines preceding/proceeding; and other threads can still generate garbage while the test code is underway. I ameliorated this by running each test thousands of times (and I can still say for sure that the examples do in fact generate more or less the amount of garbage reported because I understand what the code is doing), but again, this can’t be used for anything that requires 100% precision.

      Like

  1. In the first example int? is a subclass of System.Nullable and is used for DoSomething as a call-by-value, where the value is a reference, so the compiler can’t optimize easily here. So the scope of ‘int? maybeInt = 3’ is local to DoTestA, but it needs to allocate a new maybeInt for every iteration due to being passed as a ref value.

    Is what i was thinking. Correct me if i’m wrong.

    Like

Add a Comment