Random collection of notes, personal tips, and general things.

Bits & Bytes

  • Follow Us on Twitter!
  • LinkedIn

A not so obvious boxing

Boxing is the term used for when a type is converted into an object without an explicit cast.  Sometimes its pretty obvious when that's occurring, such as;

object x = 5;

And other times, it isn't all that obvious that its occurring.

Unboxing is the reverse (however an explicit cast is required when unboxing).  Boxing and unboxing add just that little bit of additional overhead in terms of processing, and in cases can cause bugs when you're not aware you're working with a boxed value.

One slightly less obvious case of boxing that I just came across is when using string.format & the new string interpolation replacement.

Have a look at the simple code below & the corresponding IL

line 33 turns into the IL listed from 149 to 155.  At 151 and 153 there's two box operations to box the int32 values that are being passed to the string format.

Same also if the string interpolation feature is used instead

If the code is changed slightly to use the ToString() method on the int values to explicitly convert them to strings first, the two box operations get removed.

I'm sure its one of those 'well, its kind of obvious when you think about it' situations - I'd just never thought to think about how the parameters for string.format got handled.

:D

 

 

 

 

Finalizers and the Garbage Collector

To review the threads for a .net application, start up ntsd and point it at a .net executable.

 Get started in the usual manner;

.symfix (to set the symbol store file path to local to the exe)

.reload to reload modules & symbols

g to start the application. 

Once the app is running, break into the debugger with Ctrl-C.

 

Load the sos debugger extensions from the same location that the clr dll is executing from;

.loadby sos clr

And now we're ready.

Review the threads for the app using the !threads debugger extension.  For some reason the first extension will always throw an exception.  Try again & below you can see one thread for the simple testing console application and the dedicated Finalizer thread responsible for executing any Finalizers.

Any object that has a finalizer defined will be put on the finalizer queue even if that object is live & not ready for collection by the garbage collector.

To review the finalizer queue & whats sitting on it, use the !FinalizeQueue extension

You can see at the moment, there's nothing in the Finalizer Queue for any of the generations (0 finalizable objects) and the F-Reachable Queue is also empty.  The F-Reachable queue is the queue where objects that the GC has determined are dead and need to have their finalizer invoked are moved to.  The F-Reachable Queue is the queue that the Finalizer thread works its way through sequentially invoking the finalizers for each object in this queue.

The test console app that is running has a simple class with a Finalizer declared

    public class File : IDisposable
    {
        private bool isDisposed;
        public string Path { get; set; }

        private void Dispose(bool isDisposing)
        {
            // cleanup resources
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        ~File()
        {
            Dispose(false);   
        }
    }

A new instance of a File object is being created in the console app and then breaking into the debugger before any other processing occurs.

       static void Main(string[] args)
        {
            Console.WriteLine("Enter any key to put finalizer object onto heap");
            var command = Console.ReadLine();

            var file = new File();

 

 The FinalizeQueue is showing that there are now 8 generation 0 objects in the FinalizerQueue - and the Statistics for all finalizable objects is showing the ConsoleApp.File object.

Getting started with ntsd - version check

ntsd is a low level debug utility for Windows.

When using ntsd to debug a .Net managed code application, care needs to be taken to use the right version of ntsd and the sos .net debugger extension

There's a 64 bit version and a 32 bit version of both ntsd and sos.dll

If the application is set (or left as default) with a configuration of AnyCPU, the output will be 32 bit.

When this is loaded into the x64 version of ntsd ...

... and the x64 version of SOS.dll is loaded ...

 

... ntsd will flag the target as x86 & attempts to use SOS will throw an error.

Either switch then to the x86 version of ntsd (C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\ntsd) and SOS.dll (.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll) or switch the .net app to be x64 instead