Purpose of GC.KeepAlive in C# Interop

In C#, GC.KeepAlive is used to prevent the garbage collector from prematurely collecting an object while its unmanaged resources or raw pointers are still in use by native code. Because the .NET Just-In-Time (JIT) compiler aggressively optimizes variable lifetimes, an object can be considered eligible for collection as soon as it is no longer referenced in managed execution, even if a native method invoked by that object is still actively running. Using GC.KeepAlive ensures the managed wrapper object—and any finalizer or disposal logic tied to it—remains alive until the unmanaged operation finishes.

The Problem: Aggressive Garbage Collection

In managed environments, the garbage collector does not inspect the internal state of unmanaged functions called via Platform Invoke (P/Invoke). It only tracks references within managed code. If a managed object extracts an internal resource—such as an IntPtr handle—and passes that handle to an unmanaged function, the managed object itself is often no longer referenced in the remainder of the caller method.

As a result, the JIT compiler can mark the managed object as dead while the native call is still executing. If a garbage collection occurs on another thread during that native call, the managed object might be finalized. If its finalizer frees the unmanaged handle, the native call will suddenly attempt to access invalid memory, causing an access violation or data corruption.

How GC.KeepAlive Resolves Premature Collection

The GC.KeepAlive method acts as an artificial reference. When called, it informs the JIT compiler that the specified object must be considered "in use" from the beginning of the routine up to the exact line where GC.KeepAlive is called.

public class NativeResourceWrapper
{
    private IntPtr _handle;

    public NativeResourceWrapper()
    {
        _handle = NativeMethods.AllocateResource();
    }

    public void ProcessData()
    {
        // Native method takes the raw handle
        NativeMethods.DoWork(_handle);

        // Prevents wrapper from being finalized while DoWork runs
        GC.KeepAlive(this);
    }

    ~NativeResourceWrapper()
    {
        NativeMethods.FreeResource(_handle);
    }
}

At runtime, GC.KeepAlive does not perform any computation; it is essentially a no-op that prevents the JIT optimizer from dropping the object reference earlier in the method.

Modern Alternative: SafeHandle

While GC.KeepAlive is the standard solution for manually managed pointers, modern .NET development often uses SafeHandle classes (such as SafeFileHandle). The P/Invoke marshaler automatically increments and decrements the reference count of a SafeHandle around unmanaged calls. This guarantees that the underlying resource cannot be closed or collected during native execution, eliminating the need to manually invoke GC.KeepAlive in most standard interop scenarios.