The Black Box of .NET Headline Animator

The Black Box of .NET

Thursday, May 17, 2012

Don't implement GetHashCode() on mutable fields/properties

CodeProject You shouldn't ever implement GetHashCode on mutable properties (properties that could be changed by someone) - i.e. non-private setters.   I've seen this done in several places and it results in very difficult to find bugs.

Here's why - imagine this scenario:
  1. You put an instance of your object in a collection which uses GetHashCode() "under the covers" or directly (Hashtable).
  2. Then someone changes the value of the field/property that you've used in your GetHashCode() implementation.
Guess what...your object is permanently lost in the collection since the collection uses GetHashCode() to find it! You've effectively changed the hashcode value from what was originally placed in the collection. Probably not what you wanted.
Share

Tuesday, April 17, 2012

How to determine which garbage collector is running

CodeProjectYou can determine which version of GC you're running via 2 methods:
  1. calling the System.Runtime.GCSettings.IsServerGC property
  2. attaching to the process using WinDbg and checking how many GC threads you have using the command "!sos.threads" without the quotes and (according to the below criteria)...
If you are running a Console app, WinForm app or a Windows Service, you will get the Workstation GC. Just because you are running on a Server OS doesn't mean that you will get the Server version of GC.
  • If your app is non-hosted on a multi-proc machine, you will get the Workstation GC - Concurrent by default.
  • If your app is hosted on a multi-proc machine, you will get the ServerGC by default.
The following apply to any given .NET Managed Process:

Workstation GC

  • Uni-processor machine
  • Always suspends threads
  • 1 Ephemeral GC Heap (SOH), 1 LOH GC Heap
  • Runs on thread that triggered GC
  • Thread priority is the same as the thread that triggered GC

Workstation GC - Concurrent

  • Only runs concurrent in Gen2/LOH (full collection)
  • Mutually exclusive with Server Mode
  • Slightly larger working set
  • GC Thread expires if not in use after a while
  • 1 Ephemeral GC Heap (SOH), 1 LOH GC Heap
  • Has a dedicated GC Thread
  • Thread priority is Normal

Server GC

  • Larger segment sizes
  • Faster than Workstation GC
  • Always suspends threads
  • 1 Ephemeral GC Heap (SOH) for each logical processor (this includes hyperthreaded), 1 LOH GC Heap for each logical processor (this includes hyperthreaded)
  • Has dedicated GC Threads
  • Thread priority is THREAD_PRIORITY_HIGHEST
There is only 1 Finalizer thread per managed process regardless of GC Mode. Even during a concurrent GC, managed threads are suspended (blocked) twice to do some phases of the GC.

A seldom known fact is that even if you try to set the Server mode of GC, you might not be running in Server GC; the GC ultimately determines which mode will be optimal for your app and WILL override your settings if it determines your ServerGC setting will negatively impact your application. Also, any hosted CLR app will have any manual GC settings overridden.

In CLR 4.0, things change just a little bit

  • Concurrent GC is now Background GC
  • Background GC only applies to Workstation GC
  • Old (Concurrent GC):
    • During a Full GC Allowed allocations up to end of ephemeral segment size
    • Otherwise, suspends all other threads
  • New (Background GC):
    • Allows for ephemeral GC’s simultaneously with Background GC if necessary
    • Performance is much faster
  • Server GC always blocks threads for collection of any generation

In CLR 4.5, things change just a little bit...again

  • Background Server GC
    • Server GC no longer blocks. Instead, it uses dedicated background GC threads that can run concurrently with user code - see MSDN: Background Server GC
Thus, in .NET 4.5+, all applications now have background GC available to them, regardless of which GC they use.

.NET 4.7.1 GC Improvements

.NET Framework 4.7.1 brings in changes in Garbage Collection (GC) to improve the allocation performance, especially for Large Object Heap (LOH) allocations. This is due to an architectural change to split the heap’s allocation lock into 2, for Small Object Heap (SOH) and LOH. Applications that make a lot of LOH allocations, should see a reduction in allocation lock contention, and see better performance. These improvements allow LOH allocations while Background GC (BGC) is sweeping SOH. Usually the LOH allocator waits for the whole duration of the BGC sweep process before it can satisfy requests to allocate memory. This can hinder performance. You can observe this problem in PerfView’s GCStats where there is an ‘LOH allocation pause (due to background GC) > 200 msec Events’ table. The pause reason is ‘Waiting for BGC to thread free lists’. This feature should help mitigate this problem.
Share

Tuesday, March 13, 2012

Don't use 'using()' with a WCF proxy


If you're trying to be a conscientious developer and making sure that you cleanup your resources - great! You are writing 'using()' blocks around all of your disposable items - great...except when the disposable item is a WCF Client/Proxy! The using() statement and the try/finally effectively have the same IL:

    // The IL for this block is effectively the same as
    // the IL for the second block below
    using (var win = new Form())
    {
    }

   
    // This is the second block
    Form f = null;
    try
    {
        f = new Form();
    }
    finally
    {
        if (f != null)
        {
            f.Dispose();
        }
    }

Here's the IL for the 'using()' block above compiled in Release mode:

     IL_0000:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
     IL_0005:  stloc.0
     .try
     {
         IL_0006:  leave.s    IL_0012
     }  // end .try
     finally
     {
         IL_0008:  ldloc.0
         IL_0009:  brfalse.s  IL_0011
         IL_000b:  ldloc.0
         IL_000c:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
         IL_0011:  endfinally
     }  // end handler


Here's the IL for the second block (try/finally) compiled in Release mode:

     IL_0012:  ldnull
     IL_0013:  stloc.1
     .try
     {
         IL_0014:  newobj     instance void [System.Windows.Forms]System.Windows.Forms.Form::.ctor()
         IL_0019:  stloc.1
         IL_001a:  leave.s    IL_0026
     }  // end .try
     finally
     {
         IL_001c:  ldloc.1
         IL_001d:  brfalse.s  IL_0025
         IL_001f:  ldloc.1
         IL_0020:  callvirt   instance void [System]System.ComponentModel.Component::Dispose()
         IL_0025:  endfinally
     }  // end handler

As you can see, the IL is nearly identical.

Well this is all fine and good but let's get back to the issue with WCF.  The problem is that if an exception is thrown during disposal of the WCF client/proxy, the channel is never closed.  Now, in general, any exception that occurs during disposal of an object is indeed undesirable.  But, in the case of WCF, multiple channels remaining open could easily cause your entire service to fall on its face - not to mention what might eventually happen to your web server.

Here is an alternative solution that can be used:

    WCFProxy variableName = null;
    try
    {
        variableName = new WCFProxy();

        // TODO code here

        variableName.Close();
    }
// if you need to catch other exceptions, do so here...
    catch (Exception)
    {
        if (variableName != null)
        {
            variableName.Abort();
        }
        throw;
    }

MSDN does have a brief on this issue which you can read here - http://msdn.microsoft.com/en-us/library/aa355056.aspx

Share

Thursday, January 26, 2012

Why you should comment your code...

Here are the most important reasons for including comments/documentation when writing code:
  1. I absolutely believe in documenting code using both XML doc comments on methods as well as unlined comments when/where necessary. This facilitates docentation files being generated automatically and can be used to create .chm files. For all of us who are lazy or those that think it takes too much time, use GhostDoc. Thus, laziness is no excuse.
  2. What if you have to maintain code that was written without comments? Do you want to waste your time digging thru code? What if it's more than just a method or class? What if its a library or framework? I personally have better things to do with my time.
  3. What about someone new to your team? What if your the new guy? Is it easier to learn it with or without documentation/comments?
  4. If you are writing a framework, library or API that will be used by other teams/API consumers, how are they supposed to know what it does without documentation? Do you expect them to dig thru your code to figure it out? What if they don't have access to the code? Would you want to have to do this?
  5. What if you find some code without comments and the code looks wrong or inefficient? It's certainly possible that it was written that way for a reason. Only comments would help.
  6. In addition to adding comments, code itself should be self documenting: use descriptive class, member, variable, parameter and method names.
When you write code, remember that you're not always going to be the one modifying, supporting it or consuming it...

Peter Ritchie has a good post about what comments are NOT for - a pretty good read: http://msmvps.com/blogs/peterritchie/archive/2012/01/30/what-code-comments-are-not-for.aspx


Share

Friday, December 9, 2011

Debugging Tools to Diagnose a Memory Leak

Debugging and diagnosing a memory leak or an OutOfMemoryException can be a daunting and challenging task. Fortunately, there are a number of tools to help; some of these are "paid license" apps, but there are even more tools that are free. There is a little bit of irony here; IMHO, the better tools are the free ones...

Free Tools
Licensed Tools
Hope this helps!


Share

Thursday, December 8, 2011

How to Tell if an Assembly is Debug or Release

The DebuggableAttribute is present if you compile in any setting for 'Debug' mode and when Release mode is selected and Debug Info set to anything other than "none". So, just looking for the presence of DebuggableAttribute is not sufficient and could be misleading. So, you could still have an assembly that is JIT optimized where the DebuggableAttribute is present in the Assembly Manifest.

First, you need to define exactly what is meant by "Debug" vs. "Release"...
  • Do you mean that the app is configured with code optimization?
  • Do you mean that you can attach the VS/JIT Debugger to it?
  • Do you mean that it generates DebugOutput?
  • Do you mean that it defines the DEBUG constant?  Remember that you can conditionally compile Methods with the System.Diagnostics.Conditional() attribute.
IMHO, when someone asks whether or not an assembly is "Debug" or "Release", they really mean if the code is optimized...

Sooo, do you want to do this manually or programmatically?

Manually:
You need to view the value of the DebuggableAttribute bitmask for the assembly's metadata.  Here's how to do it:

  1. Open the assembly in ILDASM
  2. Open the Manifest
  3. Look at the DebuggableAttribute bitmask.  If the DebuggableAttribute is not present, it is definitely an Optimized assembly.
  4. If it is present, look at the 4th byte - if it is a '0' it is JIT Optimized - anything else, it is not:
// Metadata version: v4.0.30319
....
     //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )

    Programmatically: assuming that you want to know programmatically if the code is JITOptimized, here is the correct implementation:

    void Main()
    {
    	var HasDebuggableAttribute = false;
    	var IsJITOptimized = false;
    	var IsJITTrackingEnabled = false;
    	var BuildType = "";
    	var DebugOutput = "";
    	var ReflectedAssembly = Assembly.LoadFile(@"C:\src\TMDE\Git\RedisScalingTest\bin\Release\netcoreapp3.1\RedisScalingTest.dll");
     
    	//	var ReflectedAssembly = Assembly.LoadFile(@"path to the dll you are testing");
    	object[] attribs = ReflectedAssembly.GetCustomAttributes(typeof(DebuggableAttribute), false);
     
    	// If the 'DebuggableAttribute' is not found then it is definitely an OPTIMIZED build
    	if (attribs.Length > 0)
    	{
    		// Just because the 'DebuggableAttribute' is found doesn't necessarily mean
    		// it's a DEBUG build; we have to check the JIT Optimization flag
    		// i.e. it could have the "generate PDB" checked but have JIT Optimization enabled
    		DebuggableAttribute debuggableAttribute = attribs[0] as DebuggableAttribute;
    		if (debuggableAttribute != null)
    		{
    			HasDebuggableAttribute = true;
    			IsJITOptimized = !debuggableAttribute.IsJITOptimizerDisabled;
     
    			// IsJITTrackingEnabled - Gets a value that indicates whether the runtime will track information during code generation for the debugger.
    			IsJITTrackingEnabled = debuggableAttribute.IsJITTrackingEnabled;
    			BuildType = debuggableAttribute.IsJITOptimizerDisabled ? "Debug" : "Release";
     
    			// check for Debug Output "full" or "pdb-only"
    			DebugOutput = (debuggableAttribute.DebuggingFlags &
    							DebuggableAttribute.DebuggingModes.Default) !=
    							DebuggableAttribute.DebuggingModes.None
    							? "Full" : "pdb-only";
    		}
    	}
    	else
    	{
    		IsJITOptimized = true;
    		BuildType = "Release";
    	}
     
    	Console.WriteLine($"{nameof(HasDebuggableAttribute)}{HasDebuggableAttribute}");
    	Console.WriteLine($"{nameof(IsJITOptimized)}{IsJITOptimized}");
    	Console.WriteLine($"{nameof(IsJITTrackingEnabled)}{IsJITTrackingEnabled}");
    	Console.WriteLine($"{nameof(BuildType)}{BuildType}");
    	Console.WriteLine($"{nameof(DebugOutput)}{DebugOutput}");
    }



    Share

    Friday, July 29, 2011

    My Favorite Quotes

    Here are some of my favorite quotes:

    "Debugging is twice as hard as writing code in the first place.  Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it."
        - Brian Kernighan

    "Intellectuals solve problems; geniuses prevent them."
        - Albert Einstein

    "There are three categories of open issues with our application: those that we know about, those that we don't know about, and those that we don't know that we don't know about."  Someone in the group actually asked "Can you give an example of the last one?"
        - former coworker

    "It's not a bug, it's a feature"
        - unknown

    "Bugs are a form of job security."
        - unknown

    "If debugging is the process of removing bugs, then programming must be the process of putting them in."
      - Edsger Dijkstra

    "The only difference between a beginning programmer and an experienced one is the complexity of the bugs they write."
      - Dave Black
    Share

    Monday, June 6, 2011

    Finally a good free multi-monitor tool

    timstall: Tool - WinSplit Revolution - move windows between ...: "A coworker showed me a useful (and free!) tool that conveniently positions open windows on your screen. The tool is Winsplit Revolution , ..."
    Share

    Friday, April 29, 2011

    New WinDbg Extension available

    I'm happy to announce that a new version of the WinDbg debugging extension PSSCOR2 (now called PSSCOR4) has been released.  It is available for public download here: 

    http://www.microsoft.com/downloads/en/details.aspx?FamilyID=a06a0fea-a4d4-434e-a527-d6afa2e552dd
    Share

    Thursday, April 28, 2011

    Whitepaper on the Debug Diagnostic Tool v1.1 - IIS debugging and debugdiag - Site Home - MSDN Blogs

    Whitepaper on the Debug Diagnostic Tool v1.1 - IIS debugging and debugdiag - Site Home - MSDN Blogs
    Share