This topic has not yet been rated - Rate this topic

MemoryFailPoint Class

Checks for sufficient memory resources prior to execution. This class cannot be inherited.

Namespace:  System.Runtime
Assembly:  mscorlib (in mscorlib.dll)
public sealed class MemoryFailPoint : CriticalFinalizerObject, 
	IDisposable

The MemoryFailPoint type exposes the following members.

  Name Description
Public method MemoryFailPoint Initializes a new instance of the MemoryFailPoint class, specifying the amount of memory required for successful execution.
Top
  Name Description
Public method Dispose Releases all resources used by the MemoryFailPoint.
Public method Equals(Object) Determines whether the specified Object is equal to the current Object. (Inherited from Object.)
Protected method Finalize Releases all the resources used by the CriticalFinalizerObject class. (Inherited from CriticalFinalizerObject.)
Public method GetHashCode Serves as a hash function for a particular type. (Inherited from Object.)
Public method GetType Gets the Type of the current instance. (Inherited from Object.)
Protected method MemberwiseClone Creates a shallow copy of the current Object. (Inherited from Object.)
Public method ToString Returns a string that represents the current object. (Inherited from Object.)
Top
Note Note

This class is intended for use in advanced development.

Creating an instance of a MemoryFailPoint class creates a memory gate. A memory gate is a check for sufficient resources prior to initiating an activity requiring a large amount of memory. Failing the check throws an InsufficientMemoryException that avoids starting an operation, reducing the possibility of an application failing during execution due to lack of resources. This allows an application to decrease its performance in an effort to avoid an OutOfMemoryException and any state corruption that may result from improper handling of an OutOfMemoryException in arbitrary locations in code.

By throwing an InsufficientMemoryException, an application can distinguish between a recoverable estimate that an operation will not be able to complete as opposed to an OutOfMemoryException during a partially completed operation that may corrupt state. This allows an application to reduce the frequency of a pessimistic escalation policy, which may require unloading the current AppDomain or recycling the process.

MemoryFailPoint checks to see whether sufficient memory and consecutive virtual address space is available in all garbage collection heaps, and may grow the swap file if required. The MemoryFailPoint makes no guarantees regarding the long-term availability of the memory during the lifetime of the gate but callers should always call Dispose to ensure resources associated with the MemoryFailPoint are released.

To use a memory gate, you must create a MemoryFailPoint object, specifying the number of megabytes of memory that the operation is expected to use. If enough memory is not available, an InsufficientMemoryException is raised.

The parameter of the constructor must be a positive integer. A negative value raises an ArgumentOutOfRangeException.

MemoryFailPoint is designed to allow an application to slow itself to avoid running out of memory in a corrupting manner. It should be used within a lexical scope. The following code example launches threads to process items in a work queue. Before each thread is launched the available memory resources are checked using MemoryFailPoint. If an exception is thrown, the main method waits until memory is available before launching the next thread.


using System;
using System.Runtime;
using System.IO;
using System.Threading;
using System.Collections.Generic;
using System.Collections;

class MemoryFailPointExample
{
    // Allocate in chunks of 64 megabytes.
    private const uint chunkSize = 64 << 20;
    // Use more than the total user-available address space (on 32 bit machines)
    // to drive towards getting an InsufficientMemoryException.
    private const uint numWorkItems = 1 + ((1U << 31) / chunkSize);
    static Queue workQueue = new Queue(50);

    // This value can be computed separately and hard-coded into the application.
    // The method is included to illustrate the technique.
    private static int EstimateMemoryUsageInMB()
    {
        int memUsageInMB = 0;

        long memBefore = GC.GetTotalMemory(true);
        int numGen0Collections = GC.CollectionCount(0);
        // Execute a test version of the method to estimate memory requirements.
        // This test method only exists to determine the memory requirements.
        ThreadMethod();
        // Includes garbage generated by the worker function.
        long memAfter = GC.GetTotalMemory(false);
        // If a garbage collection occurs during the measuring, you might need a greater memory requirement.
        Console.WriteLine("Did a GC occur while measuring?  {0}", numGen0Collections == GC.CollectionCount(0));
        // Set the field used as the parameter for the MemoryFailPoint constructor.
        long memUsage = (memAfter - memBefore);
        if (memUsage < 0)
        {
            Console.WriteLine("GC's occurred while measuring memory usage.  Try measuring again.");
            memUsage = 1 << 20;
        }

        // Round up to the nearest MB.
        memUsageInMB = (int)(1 + (memUsage >> 20));
        Console.WriteLine("Memory usage estimate: {0} bytes, rounded to {1} MB", memUsage, memUsageInMB);
        return memUsageInMB;
    }

    static void Main()
    {
        Console.WriteLine("Attempts to allocate more than 2 GB of memory across worker threads.");
        int memUsageInMB = EstimateMemoryUsageInMB();

        // For a production application consider using the threadpool instead.
        Thread[] threads = new Thread[numWorkItems];
        // Create a work queue to be processed by multiple threads.
        int n = 0;
        for (n = 0; n < numWorkItems; n++)
            workQueue.Enqueue(n);
        // Continue to launch threads until the work queue is empty.
        while (workQueue.Count > 0)
        {
            Console.WriteLine(" GC heap (live + garbage): {0} MB", GC.GetTotalMemory(false) >> 20);
            MemoryFailPoint memFailPoint = null;
            try
            {
                // Check for available memory.
                memFailPoint = new MemoryFailPoint(memUsageInMB);
                n = (int)workQueue.Dequeue();
                threads[n] =
                    new Thread(new ParameterizedThreadStart(ThreadMethod));
                WorkerState state = new WorkerState(n, memFailPoint);
                threads[n].Start(state);
                Thread.Sleep(10);
            }
            catch (InsufficientMemoryException e)
            {
                // MemoryFailPoint threw an exception, handle by sleeping for a while,  then 
                // continue processing the queue.
                Console.WriteLine("Expected InsufficientMemoryException thrown.  Message: " + e.Message);
                // We could optionally sleep until a running worker thread 
                // has finished, like this:  threads[joinCount++].Join();
                Thread.Sleep(1000);
            }
        }

        Console.WriteLine("WorkQueue is empty - blocking to ensure all threads quit (each thread sleeps for 10 seconds)");
        foreach (Thread t in threads)
            t.Join();
        Console.WriteLine("All worker threads are finished - exiting application.");
    }

    // Test version of the working code to determine memory requirements.
    static void ThreadMethod()
    {
        byte[] bytes = new byte[chunkSize];
    }

    internal class WorkerState
    {
        internal int _threadNumber;
        internal MemoryFailPoint _memFailPoint;

        internal WorkerState(int threadNumber, MemoryFailPoint memoryFailPoint)
        {
            _threadNumber = threadNumber;
            _memFailPoint = memoryFailPoint;
        }

        internal int ThreadNumber
        {
            get { return _threadNumber; }
        }

        internal MemoryFailPoint MemoryFailPoint
        {
            get { return _memFailPoint; }
        }
    }

    // The method that does the work.
    static void ThreadMethod(Object o)
    {
        WorkerState state = (WorkerState)o;
        Console.WriteLine("Executing ThreadMethod, " +
            "thread number {0}.", state.ThreadNumber);
        byte[] bytes = null;
        try
        {
            bytes = new byte[chunkSize];
            // Allocated all the memory needed for this workitem.
            // Now dispose of the MemoryFailPoint, then process the workitem.
            state.MemoryFailPoint.Dispose();
        }
        catch (OutOfMemoryException oom)
        {
            Console.Beep();
            Console.WriteLine("Unexpected OutOfMemory exception thrown: " + oom);
        }

        // Do work here, possibly taking a lock if this app needs 
        // synchronization between worker threads and/or the main thread.

        // Keep the thread alive for awhile to simulate a running thread.
        Thread.Sleep(10000);

        // A real thread would use the byte[], but to be an illustrative sample,
        // explicitly keep the byte[] alive to help exhaust the memory.
        GC.KeepAlive(bytes);
        Console.WriteLine("Thread {0} is finished.", state.ThreadNumber);

    }
}


.NET Framework

Supported in: 4, 3.5, 3.0, 2.0

.NET Framework Client Profile

Supported in: 4, 3.5 SP1

Windows 7, Windows Vista SP1 or later, Windows XP SP3, Windows XP SP2 x64 Edition, Windows Server 2008 (Server Core not supported), Windows Server 2008 R2 (Server Core supported with SP1 or later), Windows Server 2003 SP2

The .NET Framework does not support all versions of every platform. For a list of the supported versions, see .NET Framework System Requirements.
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Did you find this helpful?
(1500 characters remaining)
Community Content Add
Annotations FAQ
Disposal is important
From the documentation, you might think you could abandon these guys and not worry, but no. While MemoryFailPoint does not reserve memory for the caller, it pretends to: it keeps track of the outstanding total memory claimed by undisposed fail-point objects. It behaves as if this much memory was actually allocated already. Nothing else notices this, but any code that uses MemoryFailPoint can be affected.

This means you wan to Dispose() your MemoryFailPoint soon after actually allocating the memory; until you do, MemoryFailPoint will effectively double count that memory.

This is probably a good thing; double counting like this may make a memory fail point fail pointedly, but never a real allocation. Memory fail point's exceptions are easy to recover from, so this is a pretty safe way to go.