Thursday, 27 October 2011

The Design of Everyday Things or It's Not My Fault

A few months ago I read a book that appeared on a few developers' Must Read lists; The Design of Everyday Things, by Donald Norman. It's a cracker, I'd recommend it to anyone to have a read.

The main message that I took from the book is that when you interact with an object that someone has designed, if your interaction with the object goes wrong again and again, it's not your fault. The fault is with the designer of the object, who clearly didn't put enough effort into designing the human interaction or listen to feedback from users of the object.

As an example, take my microwave oven. One easy-to-understand concept of ovens is the hotter it is, the quicker it performs its function. Linked to this easy-to-understand concept is another, namely that the higher the number the hotter the oven: Gas Mark 6 is hotter than Gas Mark 4; 210°C is hotter than 180°C; 850W is hotter than 650W.
My microwave oven has a grill function built in, so when I fancy cheese on toast I fire it up. The power options available for the grill are 1,2 or 3. Given the convention, 3 is higher than 1, 3 must be the hottest. It must be, that's the convention. Wrong. My cheese on toast, instead of being melted bubbling cheese on piping-hot toasty bread, is sweaty slightly-warmer-than-when-it-went-in cheese on lukewarm toast.

Apart from using a set of numbers that are counter-intuitive, there's another way I could understand what power I'd selected; the designer could have used the LED screen to display "LOW", "MED" or "HI", depending on the number of button presses. This would give me instant feedback of what my repeated button presses were likely to make my cheese do. As Donald Norman would say, it's knowledge in the world as opposed to knowledge in the head; there's should be no reason for me to remember 1=high and 3=low.

The niggly grill power issue annoyed a few times until I came up with my own solution - I added a label next to the grill power-select button. (See image above).

I thought my days of being annoyed at the stupidity of the microwave designers were over until tonight. We decided we'd have jacket potatoes as part of our dinner. The oven was hot with a pie in it, so I thought we could cook the potatoes in the microwave and crisp them up in the oven. 10 minutes should do it. This leads me to another convention that I've learned with my microwave; the number of minutes you want to cook things for corresponds exactly to the number of presses of the "1 min" button; a can of soup is 2 minutes (2 presses), remove to stir, 2 more minutes (2 more presses). A microwave dinner is 6 minutes (6 presses). Jacket potatoes, 10 minutes (10 presses). No, no, no.

It turns out that if you want 10 minutes, you have to (not "can", but "have to") press the "10 min" button. After 9 presses of the "1 min" button, the LED screen shows "9 min". The next press, which I would expect to make the screen show "10 min", makes the screen show nothing. It completely ignores the fact that you've pressed the button 9 times already. Instead of cleverly [intuitively?] adding another 1 to the number of minutes cooking time and displaying 10, or even ignoring the next press of the 1 button, it has been programmed to erase any memory of the previous button presses.

As Donald Norman says, it's not my fault. It's the fault of the engineer who learnt the interface during design and testing of the microwave, and didn't bother to send it out for usability testing...

Wednesday, 26 October 2011

System.GC Methods and Properties

disclaimer: Apologies for the clinical nature of this post; any ideas on how to make the GC exciting or even interesting would be gratefully received.

There is a single property on the System.GC class
int MaxGeneration { get; }
This returns the maximum generation of objects the GC currently supports. See my post here for notes on generational GC.

System.GC implements the following methods (note, all are implemented as static methods):
void AddMemoryPressure(int bytesAllocated)
The GC.AddMemoryPressure method is used to inform the GC that a large unmanaged memory allocation has been performed. In normal circumstances, a managed object will use only managed memory, which will be kept track of by the GC. However, if a managed object allocates a large amount of unmanaged memory, AddMemoryPressure should be called to indicate to the GC that there is extra pressure on system memory. Doing so causes the GC to keep better track of the urgency of a collection. Keeping track of unmanaged memory pressure is particularly important when the object deallocates its unmanaged memory in its Finalize() method.
If applicable, AddMemoryPressure can be called once on each allocation of unmanaged memory.

void RemoveMemoryPressure(int bytesAllocated)
If a manual deallocation of unmanaged memory is performed, GC.RemoveMemoryPressure should be called to inform the GC of the reduction in the urgency of a collection.
This is the complementary method to AddMemoryPressure.

NB You should ensure that you remove exactly the same amount of pressure that you add. Failing to do so can adversely affect the performance of the system, particularly in applications that run for long periods of time.

void Collect()
This forces the GC to perform a collection. Memory for all unreachable objects in all generations will be reclaimed.

void Collect(int generation)
This overload forces the GC to collect memory, up to and including the specified generation.

void Collect(int generation, GCCollectionMode mode)
This overload forces the GC to collect memory, up to and including the specified generation.
    GCCollectionMode is one of:
    Default - currently equivalent to Forced
    Forced - Forces the garbage collection to occur immediately.
    Optimized - Allows the garbage collector to determine whether the current time is optimal to perform GC.

int CollectionCount(int generation)
This returns the count of collections that have occurred for the specified generation.

int GetGeneration(Object obj)
This returns the GC generation of the object.

int GetGeneration(WeakReference wo)
This overload returns the generation of the WeakReference specified (For the best description of how weak references work, see the section in this article Automatic Memory Management in the Microsoft .NET Framework, Part 2, and a code example).

int GetTotalMemory(bool forceFullCollection)
This returns the number of bytes currently believed to be allocated. The forceFullCollection parameter specifies whether GC should perform a collection before returning the approximate number of bytes allocated.

void KeepAlive(Object obj)
This is used to create a reference to an object in order to avoid it being collected by the GC. One reason for doing this is if the object is referenced in unmanaged code but not in managed; in normal circumstances this object would be available for collection by the GC, which would invalidate the unmanaged reference.
NB surprisingly, the call to KeepAlive should be performed at the point the object is no longer required to be kept alive.

void SuppressFinalize(Object obj)
This informs the GC that the Finalize method should not be called on obj. This should be used when all external resources have been disposed by obj. See my post here for notes on the finalizer.

void ReRegisterForFinalize(Object obj)
This informs the GC that it should call the Finalize method for obj, in spite of it having been passed to the SuppressFinalize method. See my post here for notes on the finalizer.

void WaitForPendingFinalizers()
This forces the current thread to wait until the finalization thread has finished calling the Finalize method of all finalizable objects. See my post here for notes on the finalizer.

void RegisterForFullGCNotification(int maxGenerationThreshold, int largeObjectHeapThreshold)
void CancelFullGCNotification()
GCNotificationStatus WaitForFullGCApproach()
GCNotificationStatus WaitForFullGCApproach(int millisecondsTimeout)
GCNotificationStatus WaitForFullGCComplete()
GCNotificationStatus WaitForFullGCComplete(int millisecondsTimeout)
These methods are used to register and unregister for notifications from the GC when it is about to perform a collection, and when it has completed a collection.
The example usage that Microsoft give here has the following pattern:
    Register for GC notification
    Start a thread dedicated to monitoring the GC, which will
        call WaitForFullGCApproach, and when a notification is raised
        call a static method in the class to signal a GC is approaching, in order to
        determine if a manual collection is required
    Cancel GC notification
GCNotificationStatus contains the following values:

Reference articles:
Microsoft documentation on System.GC
Microsoft article on Garbage Collection

Sunday, 23 October 2011


disclaimer: I don't usually spell finalize with a 'z' (pronounced 'zed'), but I have done so throughout this article to keep things consistent with official documentation, and so as not to confuse any of our friends from the U.S.

The Finalize() Method
C# appears to allow the use of destructors, but under the hood code that looks like this:
class FinalizeClass
        // Do stuff
actually gets compiled into code like this:
class FinalizeClass
    protected override void Finalize()
            // Do stuff
The Finalize() method is a special method that you as a developer can't override - it is created by the compiler from the definition of a destructor. It can only be called by the GC.

Creating a Finalizable Object
When the GC allocates the memory for an object that implements Finalize(), it is not created as generation 0, but as an older generation. This is also true for all objects that the finalizable object references. This is an optimisation, as there will be more to do when it eventually comes to reclaim the memory used.
On creation, the GC puts a reference to the object on the Finalization queue; this queue is effectively a list of objects that need Finalize() called on deallocation.

GC of a Finalizable Object
When the GC calculates that an object is unreachable, it searches for a reference to that object on the finalization queue; if found, the reference is moved to a different queue, the "freachable" queue (F-reachable). This is a list of objects that are ready to have their Finalize() methods called.
At this stage, the object has not had its memory reclaimed; this is done by a thread dedicated to the purpose of calling Finalize() methods. Only when this has been done is the object truly unreachable, so it takes another round of GC for its memory to be fully reclaimed.

Guaranteed Order of Execution?
There is no guarantee of the order that Finalize() methods are called: if ObjectA has a reference to ObjectB, ObjectB's Finalize() may already have been called by the time ObjectA's Finalize() is called. ObjectB is still available to ObjectA, but has already been finalized so may produce unexpected results.

Resurrection of a Dead Finalizable Object
When an object is unreachable, it is assumed to be dead by the GC. In the normal course of events, it would be resurrected in order to call its Finalize() method, then die again. However, consider the following code:
public class MyClass
        // This resurrects the object
        StaticClass.StaticObjectReference = this;
The class creates a reference to itself in a global static variable. This resurrects the object, as it is now reachable; however, its Finalize() method has been called, so using the object could cause unexpected results. The resurrection of the MyClass object also causes the resurrection of each object it references, but these may also have been finalized...

If the object is resurrected intentionally during its finalization, the developer is able to ensure that this "lifetime" will be finalized too, by calling GC.ReRegisterForFinalize():
public class MyClass
        // This resurrects the object
        StaticClass.StaticObjectReference = this;

        // This ensures the object will be finalized 
        // next time it is collected
Beware of calling GC.ReRegisterForFinalize() multiple times, as each call will cause an eventual call to the Finalize() method.

If your object implements a Finalize() method but also has an alternative method of releasing resources (for example, Dispose(), Close() or something similar), an unnecessary Finalize() call can be avoided by a call to GC.SuppressFinalize(). This informs the GC that the object does not require its Finalize() method called.
This can be useful where an object implements both the IDisposable interface and Finalize(). Dispose() allows for deterministic deallocation of resources, and the Finalize() method is implemented as a safety net. e.g.
public class MyClass : IDisposable
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
        if (!disposed)
            if (disposing)
                // called via myClass.Dispose().
                // OK to use any private object references
            disposed = true;

    public void Dispose() // Implementation of IDisposable

    ~MyClass() // the finalizer
Reference articles:
MSDN Library article on Garbage Collection

MSDN magazine articles:
Automatic Memory Management in the Microsoft .NET Framework, Part 1
Automatic Memory Management in the Microsoft .NET Framework, Part 2

StackOverflow - When should I use GC.SuppressFinalize()?

Next time: System.GC - an explanation of those hard-to-understand methods

.Net Garbage Collection, Fundamentals

The Managed Heap
The managed heap is an area of memory which is controlled by the .Net CLR. It holds the memory allocated for each object created in the .Net managed environment.
The heap consists of 2 sub-heaps, the small object heap and the large object heap. Objects larger than 85k go to the large object heap.

The Garbage Collector
The garbage collector (GC from now on) controls the allocation, deallocation and recycling of unused memory on the managed heap.

Object Roots
The JIT compiler keeps a note of the roots of each object on the managed heap; a root is effectively a holder of a reference to an object on the heap. An object can be referenced by, among others, static objects, local objects, parameters and pointers on CPU registers.

Garbage Collection
When the GC performs a collection, it searches for objects with no roots, and these are deemed unreachable. As any object could also be referenced by other objects on the heap, this search is performed recursively. When the list of reachable objects is identified, the GC compacts them and moves them to the lowest possible memory address into a contiguous block (think defragmentation of a hard drive). All references to the remaining objects are updated to the new memory addresses, and the pointer to the next available memory address is updated.

Object Creation
When an object reference is created, the GC attempts to allocate a block of contiguous memory on the heap; if there is no block available, the GC performs a collection. If there is still no block available, an OutOfMemoryException is thrown.

Object Generations
When the managed heap is initialised, it contains no objects; as each new small object is added, it is deemed to be in generation 0. Any large object will be added to the large object heap as generation 2. When the heap fills and a GC is required, all surviving objects are promoted to generation 1. The next time the GC compacts, any surviving generation 1 objects are promoted to generation 2. This is the highest generation that is used by the GC.
This generational GC allows optimisation, by inspecting only the lowest generations required to free enough memory to allow new objects to be created.
The assumption behind this optimisation is that new objects are expected to be short-lived, and that objects created at the same time are likely to be used then destroyed together.

Forcing a Collection
As a developer, if you need to force the GC to perform a garbage collection (for example, you have a complex form with lots of controls that has closed any you want to immediately reclaim the memory), call the System.GC.Collect() method. Overloads of this method allows you to specify the maximum generation of objects to collect, so specifying 1 would force the GC to collect generations 0 and 1. Another overload allows the developer to specify the collection mode, Forced, Optimized or Default. Optimized allows the GC to determine if a collection is required.

Reference articles:
MSDN Library article on Garbage Collection

MSDN magazine articles:
Automatic Memory Management in the Microsoft .NET Framework, Part 1
Automatic Memory Management in the Microsoft .NET Framework, Part 2

Next time... Finalize() - when and why you should avoid it