Export (0) Print
Expand All
1 out of 1 rated this helpful - Rate this topic

GC.KeepAlive Method

References the specified object, making it ineligible for garbage collection from the start of the current routine to the point where this method is called.

Namespace: System
Assembly: mscorlib (in mscorlib.dll)

public static void KeepAlive (
	Object obj
)
public static void KeepAlive (
	Object obj
)
public static function KeepAlive (
	obj : Object
)

Parameters

obj

The object to reference.

The purpose of the KeepAlive method is to ensure the existence of a reference to an object that is at risk of being prematurely reclaimed by the garbage collector. A common scenario where this might happen is when there are no references to the object in managed code or data, but the object is still in use in unmanaged code such as Win32 APIs, unmanaged DLLs, or methods using COM.

Another case of premature garbage collection occurs when an object is created and used within a method. The object might be reclaimed while a call to one of its members is still executing, as shown in the first code example.

This method references obj, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available.

The KeepAlive method performs no operation and produces no side effects other than extending the lifetime of the object passed in as a parameter.

This section contains two code examples. The first example demonstrates the use of the KeepAlive method to prevent aggressive garbage collection, and the second example demonstrates the use of the KeepAlive method in a long-running method.

Example 1

The following code example shows how the KeepAlive method prevents aggressive garbage collection from occurring while a method of the collected object is still executing.

NoteNote

This example requires a computer with multiple processors.

The example starts a thread that repeatedly calls a test method. The thread executes until you press the ENTER key. By default, the code example runs the DoWork method, which creates an Example object and reads its Hash property. Because the DoWork method does not use the KeepAlive method, the finalizer for the Example object sometimes runs before the property has been read. The DoWork method detects this and displays a console message.

When executed with the argument KeepAlive, the example runs the SafeDoWork method. The SafeDoWork method is identical to the DoWork method except for the last line, which calls the KeepAlive method. This prevents the Example object from being reclaimed until the end of the SafeDoWork method.

When executed with the argument Trivial, the example runs the Trivial method, which accesses an integer property that is much faster than the Hash property. Because access is so fast, the finalizer almost never runs first.

using System;
using System.Threading;

public class Example 
{
    // The N property is very fast, because all it does is return
    // a stored integer. Therefore the finalizer almost never runs
    // before this property is read.
    //
    private int nValue;
    public int N { get { return nValue; }}

    // The Hash property is slower because it clones an array. When
    // KeepAlive is not used, the finalizer sometimes runs before 
    // the Hash property value is read.
    //
    private byte[] hashValue;
    public byte[] Hash { get { return (byte[]) hashValue.Clone(); }}
    
    // The constructor initializes the property values.
    //
    public Example() 
    {
        nValue = 2;

        hashValue = new byte[20];
        hashValue[0] = 2;
    }

    // The finalizer sets the N property to zero, and clears all 
    // the elements of the array for the Hash property. The finalizer
    // runs on a separate thread provided by the system. In some
    // cases, the finalizer can run while a member of the Example
    // instance is still executing.
    //
    ~Example() 
    {
        nValue = 0;
        if (hashValue != null)
        {
            Array.Clear(hashValue, 0, hashValue.Length);
        }
    }
}

public class Test 
{
    private static int totalCount = 0;
    private static int finalizerFirstCount = 0;

    // This variable controls the thread that runs the demo.
    private static bool running = true;

    // The default is to run without KeepAlive.
    private static TestKind kind = TestKind.NoKeepAlive;

    // See the comment at the end of the SafeDoWork method.
    //private static bool keepAlive = false;

    // In order to demonstrate the finalizer running first, the
    // DoWork method must create an Example object and invoke its
    // Hash property. If there are no other calls to members of
    // the Example object in DoWork, garbage collection reclaims
    // the Example object aggressively. Sometimes this means that
    // the finalizer runs before the call to the Hash property
    // completes. 
    private static void DoWork() 
    {
        totalCount++;

        // Create an Example object and save the value of the 
        // Hash property. There are no more calls to members of 
        // the object in the DoWork method, so it is available
        // for aggressive garbage collection.
        //
        Example ex = new Example();
        byte[] res = ex.Hash;

        // If the finalizer runs before the call to the Hash 
        // property completes, the hashValue array might be
        // cleared before the property value is read. The 
        // following test detects that.
        //
        if (res[0] != 2) 
        {
            finalizerFirstCount++;
            Console.WriteLine("The finalizer ran first at {0} iterations.",
                totalCount);
        }
    }

    // In the SafeDoWork method the finalizer never runs first,
    // because of the KeepAlive at the end of the method.
    //    
    private static void SafeDoWork() 
    {
        totalCount++;

        // Create an Example object and save the value of the 
        // Hash property.
        Example ex = new Example();
        byte[] res = ex.Hash;

        // The finalizer cannot run before the property is read,
        // because the KeepAlive method prevents the Example object
        // from being reclaimed by garbage collection.
        //
        if (res[0] != 2) 
        {
            finalizerFirstCount++;
            Console.WriteLine("The finalizer ran first at {0} iterations.",
                totalCount);
        }
        
        GC.KeepAlive(ex);
        // The KeepAlive method need never be executed. For example,
        // if the keepAlive field is uncommented, the following line
        // of code prevents the finalizer from running first, even
        // though it is impossible for the KeepAlive method ever to
        // be executed.
        //    if (keepAlive) GC.KeepAlive(ex);
        // However, if the compiler detects that the KeepAlive can
        // never be executed, as in the following line, then it will
        // not prevent the finalizer from running first.
        //    if (false) GC.KeepAlive(ex);
    }

    // In the TrivialDoWork method the finalizer almost never runs
    // first, even without a KeepAlive at the end of the method,
    // because accessing the N property is so fast.
    //    
    private static void TrivialDoWork() 
    {
        totalCount++;

        // Create an Example object and save the value of the 
        // N property.
        Example ex = new Example();
        int res = ex.N;

        // The finalizer almost never runs before the property is read,
        // because accessing the N property is so fast.
        //
        if (res != 2) 
        {
            finalizerFirstCount++;
            Console.WriteLine("The finalizer ran first at {0} iterations.",
                totalCount);
        }        
    }

    public static void Main (string[] args) 
    {
        if (args.Length != 0)
        {
            string arg = args[0].ToLower();
            if (arg.Length < 10 && arg == "keepalive".Substring(0, arg.Length)) 
                kind = TestKind.KeepAlive;
            if (arg.Length < 8 && arg == "trivial".Substring(0, arg.Length)) 
                kind = TestKind.Trivial;
        }
        Console.WriteLine("Test: {0}", kind);

        // Create a thread to run the test.
        Thread t = new Thread(new ThreadStart(ThreadProc));
        t.Start();

        // The thread runs until Enter is pressed.
        Console.WriteLine("Press Enter to stop the program.");
        Console.ReadLine();
        running = false;

        // Wait for the thread to end.
        t.Join();

        Console.WriteLine("{0} iterations total; the finalizer ran first {1} times.",
            totalCount, finalizerFirstCount);
    }

    private static void ThreadProc()
    {
        switch (kind)
        {
            case TestKind.KeepAlive:
                while (running) SafeDoWork();
                break;

            case TestKind.Trivial:
                while (running) TrivialDoWork();
                break;

            default: 
                while (running) DoWork();
                break;
        }
    }

    private enum TestKind
    {
        NoKeepAlive,
        KeepAlive,
        Trivial
    }
}
/* When run with the default NoKeepAlive test, on a dual-processor
   computer, this example produces output similar to the following:

Test: NoKeepAlive
Press Enter to stop the program.
The finalizer ran first at 21098618 iterations.
The finalizer ran first at 33944444 iterations.
The finalizer ran first at 35160207 iterations.

53169451 iterations total; the finalizer ran first 3 times.
*/

Example 2

The following code example creates an object at the beginning of its Main method and does not refer to the object again until the end, when the KeepAlive method is called. The object persists for the 30-second duration of the Main method, despite calls to the Collect and WaitForPendingFinalizers methods.

using System;
using System.Threading;
using System.Runtime.InteropServices;

// A simple class that exposes two static Win32 functions.
// One is a delegate type and the other is an enumerated type.
public class MyWin32 
{
    // Declare the SetConsoleCtrlHandler function 
    // as external and receiving a delegate.   
    [DllImport("Kernel32")] 
    public static extern Boolean SetConsoleCtrlHandler(HandlerRoutine Handler, 
        Boolean Add);

    // A delegate type to be used as the handler routine 
    // for SetConsoleCtrlHandler.
    public delegate Boolean HandlerRoutine(CtrlTypes CtrlType);

    // An enumerated type for the control messages 
    // sent to the handler routine.
    public enum CtrlTypes 
    {
        CTRL_C_EVENT = 0,
        CTRL_BREAK_EVENT,
        CTRL_CLOSE_EVENT,   
        CTRL_LOGOFF_EVENT = 5,
        CTRL_SHUTDOWN_EVENT
    }
}

public class MyApp 
{
    // A private static handler function in the MyApp class.
    static Boolean Handler(MyWin32.CtrlTypes CtrlType)
    {
        String message = "This message should never be seen!";

        // A switch to handle the event type.
        switch(CtrlType)
        {
            case MyWin32.CtrlTypes.CTRL_C_EVENT:
                message = "A CTRL_C_EVENT was raised by the user.";
                break;
            case MyWin32.CtrlTypes.CTRL_BREAK_EVENT:
                message = "A CTRL_BREAK_EVENT was raised by the user.";
                break;
            case MyWin32.CtrlTypes.CTRL_CLOSE_EVENT:   
                message = "A CTRL_CLOSE_EVENT was raised by the user.";
                break;
            case MyWin32.CtrlTypes.CTRL_LOGOFF_EVENT:
                message = "A CTRL_LOGOFF_EVENT was raised by the user.";
                break;
            case MyWin32.CtrlTypes.CTRL_SHUTDOWN_EVENT:
                message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
                break;
        }

        // Use interop to display a message for the type of event.
        Console.WriteLine(message);

        return true;
    }

    public static void Main()
    {         

        // Use interop to set a console control handler.
        MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler);
        MyWin32.SetConsoleCtrlHandler(hr, true);

        // Give the user some time to raise a few events.
        Console.WriteLine("Waiting 30 seconds for console ctrl events...");

        // The object hr is not referred to again.
        // The garbage collector can detect that the object has no
        // more managed references and might clean it up here while
        // the unmanaged SetConsoleCtrlHandler method is still using it.      
		
        // Force a garbage collection to demonstrate how the hr
        // object will be handled.
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
		
        Thread.Sleep(30000);

        // Display a message to the console when the unmanaged method
        // has finished its work.
        Console.WriteLine("Finished!");

        // Call GC.KeepAlive(hr) at this point to maintain a reference to hr. 
        // This will prevent the garbage collector from collecting the 
        // object during the execution of the SetConsoleCtrlHandler method.
        GC.KeepAlive(hr);   
        Console.Read();
    }
}

import System.* ;
import System.Threading.* ;
import System.Runtime.InteropServices.* ;

// A simple class that exposes two static Win32 functions.
// One is a delegate type and the other is an enumerated type.
public class MyWin32
{
    // Declare the SetConsoleCtrlHandler function 
    // as external and receiving a delegate.   

    /** @attribute DllImport("Kernel32")
     */
    public static native boolean SetConsoleCtrlHandler(HandlerRoutine Handler, 
        boolean Add);
    
    /** @delegate 
     */
    // A delegate type to be used as the handler routine 
    // for SetConsoleCtrlHandler.
    public delegate boolean HandlerRoutine(int CtrlType);

    // The control messages sent to the handler routine.
    public static int CtrlTypes[] = new int[] { 0,/*CTRL_C_EVENT*/
                                                1,/*CTRL_BREAK_EVENT*/
                                                2,/*CTRL_CLOSE_EVENT*/
                                                5,/*CTRL_LOGOFF_EVENT*/
                                                6 /*CTRL_SHUTDOWN_EVENT*/ };
} //MyWin32

public class MyApp
{
    // A private static handler function in the MyApp class.
    static boolean Handler(int CtrlType)
    {
        String message = "This message should never be seen!";

        if ( MyWin32.CtrlTypes[0] == CtrlType) { 
            message = "A CTRL_C_EVENT was raised by the user.";
        }
        
        if (MyWin32.CtrlTypes[1] == CtrlType) { 
            message = "A CTRL_BREAK_EVENT was raised by the user.";
        }
        
        if (MyWin32.CtrlTypes[2] == CtrlType) { 
            message = "A CTRL_CLOSE_EVENT was raised by the user.";
        }
        
        if ( MyWin32.CtrlTypes[3] == CtrlType) { 
            message = "A CTRL_LOGOFF_EVENT was raised by the user.";
        }
        
        if (MyWin32.CtrlTypes[4] == CtrlType) {
            message = "A CTRL_SHUTDOWN_EVENT was raised by the user.";
        }
        
        // Use interop to display a message for the type of event.
        Console.WriteLine(message);
        
        return true ;
    } //Handler
   
    public static void main(String[] args)
    {
        // Use interop to set a console control handler.
        MyWin32.HandlerRoutine hr =  new MyWin32.HandlerRoutine(Handler);
        MyWin32.SetConsoleCtrlHandler(hr, true);

        // Give the user some time to raise a few events.
        Console.WriteLine("Waiting 30 seconds for console ctrl events...");

        // The object hr is not referred to again.
        // The garbage collector can detect that the object has no
        // more managed references and might clean it up here while
        // the unmanaged SetConsoleCtrlHandler method is still using it.  
        // Force a garbage collection to demonstrate how the hr
        // object will be handled.
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        try {
            Thread.sleep(30000);
        }
        catch (InterruptedException e) {
        }

        // Display a message to the console when the unmanaged method
        // has finished its work.
        Console.WriteLine("Finished!");

        // Call GC.KeepAlive(hr) at this point to maintain a reference to hr. 
        // This will prevent the garbage collector from collecting the 
        // object during the execution of the SetConsoleCtrlHandler method.
        GC.KeepAlive(hr);
        Console.Read();
    } //main
} //MyApp

Windows 98, Windows 2000 SP4, Windows CE, Windows Millennium Edition, Windows Mobile for Pocket PC, Windows Mobile for Smartphone, Windows Server 2003, Windows XP Media Center Edition, Windows XP Professional x64 Edition, Windows XP SP2, Windows XP Starter Edition

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see System Requirements.

.NET Framework

Supported in: 2.0, 1.1, 1.0

.NET Compact Framework

Supported in: 2.0, 1.0
Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback

Community Additions

ADD
Show:
© 2014 Microsoft. All rights reserved.