Reservieren und Commiten von Arbeitsspeicher

Das folgende Beispiel veranschaulicht die Verwendung der Funktionen VirtualAlloc und VirtualFree beim Reservieren und Commiten von Arbeitsspeicher nach Bedarf für ein dynamisches Array. Zunächst wird VirtualAlloc aufgerufen, um einen Block von Seiten mit null als Basisadressenparameter zu reservieren, wodurch das System gezwungen wird, den Speicherort des Blocks zu bestimmen. Später wird VirtualAlloc aufgerufen, wenn es erforderlich ist, eine Seite aus dieser reservierten Region zu committen, und die Basisadresse der nächsten Seite, die committet werden soll, wird angegeben.

Im Beispiel wird eine strukturierte Ausnahmebehandlungssyntax verwendet, um Seiten aus der reservierten Region zu committen. Wenn während der Ausführung des __try Blocks eine Seitenfehlerausnahme auftritt, wird die Filterfunktion in dem Ausdruck ausgeführt, der dem __except-Block vorangeht. Wenn die Filterfunktion eine andere Seite zuordnen kann, wird die Ausführung im __try-Block an dem Punkt fortgesetzt, an dem die Ausnahme aufgetreten ist. Andernfalls wird der Ausnahmehandler im __except-Block ausgeführt. Weitere Informationen finden Sie unter Strukturierte Ausnahmebehandlung.

Als Alternative zur dynamischen Zuordnung kann der Prozess einfach die gesamte Region commitsieren, anstatt sie nur zu reservieren. Beide Methoden führen zu derselben physischen Speicherauslastung, da verpflichtete Seiten erst dann physischen Speicher verbrauchen, wenn sie zuerst darauf zugegriffen werden. Der Vorteil der dynamischen Zuordnung besteht darin, dass sie die Gesamtzahl der gebundenen Seiten im System minimiert. Bei sehr großen Zuordnungen kann das Pre-Commit für eine gesamte Zuordnung dazu führen, dass dem System die Committabellenseiten ausgehen, was zu Fehlern bei der Zuordnung virtueller Speicher führt.

Die ExitProcess-Funktion im __except-Block gibt automatisch virtuelle Speicherzuordnungen frei, sodass es nicht erforderlich ist, die Seiten explizit freizugeben, wenn das Programm über diesen Ausführungspfad beendet wird. Die VirtualFree-Funktion gibt die reservierten und verpflichteten Seiten frei, wenn das Programm mit deaktivierter Ausnahmebehandlung erstellt wird. Diese Funktion verwendet MEM_RELEASE , um die gesamte Region der reservierten und gebundenen Seiten zu deaktivieren und freizugeben.

Im folgenden C++-Beispiel wird die dynamische Speicherzuordnung mithilfe eines strukturierten Ausnahmehandlers veranschaulicht.

// A short program to demonstrate dynamic memory allocation
// using a structured exception handler.

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>             // For exit

#define PAGELIMIT 80            // Number of pages to ask for

LPTSTR lpNxtPage;               // Address of the next page to ask for
DWORD dwPages = 0;              // Count of pages gotten so far
DWORD dwPageSize;               // Page size on this computer

INT PageFaultExceptionFilter(DWORD dwCode)
{
    LPVOID lpvResult;

    // If the exception is not a page fault, exit.

    if (dwCode != EXCEPTION_ACCESS_VIOLATION)
    {
        _tprintf(TEXT("Exception code = %d.\n"), dwCode);
        return EXCEPTION_EXECUTE_HANDLER;
    }

    _tprintf(TEXT("Exception is a page fault.\n"));

    // If the reserved pages are used up, exit.

    if (dwPages >= PAGELIMIT)
    {
        _tprintf(TEXT("Exception: out of pages.\n"));
        return EXCEPTION_EXECUTE_HANDLER;
    }

    // Otherwise, commit another page.

    lpvResult = VirtualAlloc(
                     (LPVOID) lpNxtPage, // Next page to commit
                     dwPageSize,         // Page size, in bytes
                     MEM_COMMIT,         // Allocate a committed page
                     PAGE_READWRITE);    // Read/write access
    if (lpvResult == NULL )
    {
        _tprintf(TEXT("VirtualAlloc failed.\n"));
        return EXCEPTION_EXECUTE_HANDLER;
    }
    else
    {
        _tprintf(TEXT("Allocating another page.\n"));
    }

    // Increment the page count, and advance lpNxtPage to the next page.

    dwPages++;
    lpNxtPage = (LPTSTR) ((PCHAR) lpNxtPage + dwPageSize);

    // Continue execution where the page fault occurred.

    return EXCEPTION_CONTINUE_EXECUTION;
}

VOID ErrorExit(LPTSTR lpMsg)
{
    _tprintf(TEXT("Error! %s with error code of %ld.\n"),
             lpMsg, GetLastError ());
    exit (0);
}

VOID _tmain(VOID)
{
    LPVOID lpvBase;               // Base address of the test memory
    LPTSTR lpPtr;                 // Generic character pointer
    BOOL bSuccess;                // Flag
    DWORD i;                      // Generic counter
    SYSTEM_INFO sSysInfo;         // Useful information about the system

    GetSystemInfo(&sSysInfo);     // Initialize the structure.

    _tprintf (TEXT("This computer has page size %d.\n"), sSysInfo.dwPageSize);

    dwPageSize = sSysInfo.dwPageSize;

    // Reserve pages in the virtual address space of the process.

    lpvBase = VirtualAlloc(
                     NULL,                 // System selects address
                     PAGELIMIT*dwPageSize, // Size of allocation
                     MEM_RESERVE,          // Allocate reserved pages
                     PAGE_NOACCESS);       // Protection = no access
    if (lpvBase == NULL )
        ErrorExit(TEXT("VirtualAlloc reserve failed."));

    lpPtr = lpNxtPage = (LPTSTR) lpvBase;

    // Use structured exception handling when accessing the pages.
    // If a page fault occurs, the exception filter is executed to
    // commit another page from the reserved block of pages.

    for (i=0; i < PAGELIMIT*dwPageSize; i++)
    {
        __try
        {
            // Write to memory.

            lpPtr[i] = 'a';
        }

        // If there's a page fault, commit another page and try again.

        __except ( PageFaultExceptionFilter( GetExceptionCode() ) )
        {

            // This code is executed only if the filter function
            // is unsuccessful in committing the next page.

            _tprintf (TEXT("Exiting process.\n"));

            ExitProcess( GetLastError() );

        }

    }

    // Release the block of pages when you are finished using them.

    bSuccess = VirtualFree(
                       lpvBase,       // Base address of block
                       0,             // Bytes of committed pages
                       MEM_RELEASE);  // Decommit the pages

    _tprintf (TEXT("Release %s.\n"), bSuccess ? TEXT("succeeded") : TEXT("failed") );

}