Les profondeurs de la mémoire Windows (partie 1) — L'instant où une adresse virtuelle devient de la RAM physique : un défaut de page de bout en bout

· Mis à jour le: · · Windows, Gestion de la mémoire, VirtualAlloc, Page Fault, VAD, Supervision des performances

Historique des révisions (première version, publiée le 20 Aug 2026)
Première publication
Citer cet article(DOI (archive enregistrée): 10.5281/zenodo.22176072)

Les DOI ci-dessous renvoient à des versions déjà archivées et peuvent différer du texte actuel. Pour citer le texte actuel, utilisez l’URL de cette page.

Go Komura (2026). Les profondeurs de la mémoire Windows (partie 1) — L'instant où une adresse virtuelle devient de la RAM physique : un défaut de page de bout en bout. KomuraSoft LLC. https://comcomponent.com/fr/blog/windows-memory-internals-page-fault/

DOI (archive enregistrée)
10.5281/zenodo.22176072
DOI (dernière version enregistrée)
10.5281/zenodo.22176073

« J’ai fait un Commit de 256 MiB, mais le Working Set n’augmente pas d’autant. » Cette question, qui revient quand on utilise VirtualAlloc, est le point de départ de la partie 1.

Pour la mémoire privée ordinaire, committer une page et la placer en RAM physique sont deux choses distinctes. Windows retarde l’attribution des pages physiques jusqu’à ce que l’application touche réellement la page. Quand le premier accès provoque un défaut de page, le gestionnaire de mémoire examine le VAD, la PTE, les attributs de protection et le backing store, puis lie la RAM nécessaire une page à la fois.1

Cet article suit le chemin que « le premier octet touché » parcourt jusqu’à la RAM physique. Si vous voulez d’abord clarifier le sens des chiffres Working Set et Commit, voyez l’article d’introduction « Que représente réellement la « mémoire utilisée » sous Windows ── Bien lire Working Set, Private Bytes, Commit et le fichier d’échange ». Cette série ne redéfinit pas les termes : elle creuse, depuis le mécanisme, pourquoi ces chiffres sortent ainsi.

« Les profondeurs de la mémoire Windows » — les 3 parties

Cette série avance dans l’ordre obtenir une page physique → suivre la résidence et la récupération → comprendre le partage et la privatisation.

Partie Thème Ce que cette partie suit
Partie 1 (cet article) Adresses virtuelles et défauts de page Quand une région allouée avec VirtualAlloc obtient de la RAM physique
Partie 2 La vie d’une page physique Les transitions d’état d’une page sortie du Working Set, et le rôle du fichier d’échange
Partie 3 Objets section et copie sur écriture Comment les DLL, les mappages de fichiers et la mémoire partagée partagent des pages physiques

La question à laquelle répond la partie 1 n’est qu’une.

À quel instant une adresse virtuelle déjà en Commit devient-elle de la RAM physique ?

Avant de commencer Contenu
Public visé Développeurs et exploitants qui veulent comprendre, depuis le mécanisme, l’usage mémoire, les défauts de page juste après le démarrage, 0xC0000005, et les chiffres de VMMap et de PerfMon
Environnement Windows 10/11 ou une version actuelle de Windows Server
Prérequis Les pointeurs et les bases de VirtualAlloc
Difficulté Intermédiaire. L’expérience des dispositions de bits des tables de pages ou d’un débogueur noyau n’est pas nécessaire

Nous utilisons les noms de structures internes, mais nous n’assumons pas de dispositions non documentées qui dépendent d’une compilation Windows particulière.

Dans le diagramme, un trait continu marque une relation qui vaut toujours et un trait pointillé une relation conditionnelle (les conditions figurent dans l’explication de chaque relation sur la page de détail). La liste complète des relations (14 au total, avec preuve et niveau de certitude) et les définitions des concepts principaux sont rassemblées sur la page de détail de la carte des connaissances (en japonais). Données : JSON-LD / Turtle

1. D’abord la conclusion

Pour la mémoire privée ordinaire, séparer les trois étapes suivantes fait apparaître la différence entre Commit et Working Set.

  1. Reserve est l’étape qui réserve une adresse dans l’espace virtuel. Elle réserve la plage, mais n’attribue aucune zone de stockage physique en RAM ni dans le fichier d’échange.1
  2. Commit est l’étape où le système promet de pouvoir conserver le contenu plus tard. Il impute une charge de commit et augmente le Commit Total, mais ne lie en général pas encore de RAM physique à toutes les pages.12
  3. Touch est l’étape où une page physique devient réellement nécessaire. Le premier accès provoque un défaut de page et, si l’accès est légitime, le gestionnaire de mémoire attribue une page physique et réexécute l’instruction.

MEM_COMMIT n’est pas un ordre qui dit « alloue de la RAM tout de suite ». Ce n’est pas non plus une promesse en l’air : c’est une promesse à l’échelle du système que le contenu pourra être conservé plus tard, en RAM ou dans un backing store approprié. Le fait que le contenu initial d’une page en Commit soit zéro, et le fait qu’aucune page physique ne soit attribuée avant le premier accès, sont parfaitement compatibles.1

L’idée selon laquelle « Reserve/Commit n’écrivent que dans le VAD » est elle aussi inexacte. Reserve crée surtout un VAD qui représente la plage et ses attributs ; Commit augmente le Commit Total du système et enregistre l’état commité de la plage. Les niveaux intermédiaires de la table de pages et les PTE individuelles sont construits de façon différée, au moment où ils deviennent nécessaires.

Ce que vous voulez savoir Sections à lire
Ce qui change à Reserve, Commit et Touch Sections 2 à 3
Ce que décident le VAD, la PTE et le TLB Sections 4 à 6
Séparer les défauts normaux des attentes d’E/S et des exceptions Sections 7 à 9
Vérifier sur les chiffres de votre machine Sections 10 à 11
Ce qui se produit à Reserve, Commit et au premier accèsMEM_RESERVE enregistre la plage et les attributs dans le VAD, MEM_COMMIT consomme le Commit Total pour promettre le stockage, et le défaut de page du premier accès attribue une page physique et l'ajoute au Working Set1. MEM_RESERVE2. MEM_COMMIT3. Premier accès (Touch)Enregistrement de la plage et des attributs dans le VADConsommation du Commit Total (pas encore de page physique)Défaut de pageLiaison d'une page physique mise à zéro à la PTEAjout au Working Set et réexécution de l'instruction

Figure 1 : Reserve, Commit et Touch sont des événements distincts. La RAM physique n’est liée qu’à la dernière étape, le premier accès.

2. Trois registres pour suivre une page virtuelle

Les registres que tient Windows regardent la même mémoire selon des unités différentes. Distinguons d’abord les trois : plage, page virtuelle et page physique.

Registre Unité Rôle
VAD Plage d’adresses virtuelles Gère la nature de la région, Reserve/Commit, la protection et la correspondance avec une section
Table de pages / PTE Page virtuelle Représente la traduction actuelle vers une page physique, ou un état non encore matérialisé
Base PFN Page physique Suit la propriété, les références et l’état de chaque page de RAM

Le VAD porte l’information d’une plage, la PTE celle d’une page virtuelle, et la base PFN celle d’une page physique. Le gestionnaire de défaut de page croise ces registres pour décider si l’accès peut continuer.

Trois registres de l'adresse virtuelle à la RAM physiqueUne adresse virtuelle est gérée par le VAD à la granularité de la plage et par la PTE à la granularité de la page virtuelle, et la base PFN suit, à la granularité de la page physique, la page physique vers laquelle la PTE traduitÉvalue Reserve/Commit et la protectionTraduction valideAdresse virtuelleVAD (registre des plages)PTE (registre des pages virtuelles)Base PFN (registre des pages physiques)Page de RAM physique

Figure 2 : Trois registres à des granularités différentes. Le traitement du défaut croise le VAD et la PTE, puis applique le résultat côté PFN.

Cet article a pour protagonistes le VAD et la PTE. La partie 2 regardera la base PFN depuis le côté page physique.

3. Reserve, Commit et Touch sont des événements distincts

3.1. Reserve — réserver une adresse

Réservons d’abord une plage d’adresses virtuelles contiguë de 256 MiB.

void* base = VirtualAlloc(
    nullptr,
    256ull * 1024 * 1024,
    MEM_RESERVE,
    PAGE_NOACCESS);

À ce stade, tout ce qui s’est produit, c’est qu’une adresse a été réservée dans l’espace virtuel du processus pour que d’autres allocations ne puissent pas utiliser cette plage. MEM_RESERVE n’attribue aucune zone de stockage physique, ni en RAM ni dans le fichier d’échange.1

Parce qu’un processus 64 bits dispose d’un vaste espace virtuel, une conception qui réserve d’abord une grande plage et ne committe plus tard que les parties nécessaires devient réaliste.

3.2. Commit — promettre que le contenu pourra être conservé

Ensuite, on committe la plage déjà réservée.

void* committed = VirtualAlloc(
    base,
    256ull * 1024 * 1024,
    MEM_COMMIT,
    PAGE_READWRITE);

En cas de succès, la quantité promise augmente : elle se reflète dans le Commit Total du système et, en général, dans les Private Bytes du processus. Pour autant, 256 MiB de pages physiques ne se rangent pas d’un coup. Les pages ordinaires restent physiquement non attribuées jusqu’au premier accès.12

Alors, à quoi sert le Commit ? À ceci : si le système ne peut pas prendre la promesse, il peut renvoyer un échec au moment du Commit, et non au milieu de l’utilisation de la mémoire.

3.3. Touch — quand une page physique devient nécessaire

Enfin, l’affectation suivante écrit pour la première fois dans la page de tête.

static_cast<unsigned char*>(base)[0] = 1;

Le processeur tente de traduire l’adresse virtuelle en adresse physique, mais la PTE n’a pas encore de traduction valide vers une page physique. C’est là que le défaut de page se produit.

Le gestionnaire de mémoire, qui reçoit le contrôle, détermine qu’il s’agit d’un « premier accès à une page privée en Commit et accessible en écriture », obtient une page physique mise à zéro, la lie à la PTE et l’ajoute au Working Set. Il fait ensuite réexécuter l’instruction d’écriture qui a échoué.

Du point de vue de l’application, ce n’est qu’une affectation. En interne, le contrôle entre dans le noyau au milieu de l’affectation, une page physique est attribuée, et l’exécution revient à la même instruction.

4. Le VAD — registre des plages de l’espace virtuel

VAD est l’abréviation de Virtual Address Descriptor : Windows gère les plages d’adresses utilisées par un processus sous forme d’arbre de VAD. La commande !vad de WinDbg permet d’inspecter le VPN de début et de fin, le Commit, les attributs de protection, Private/Mapped, la Control Area, et d’autres champs.3

Les informations représentatives portées par un VAD sont les suivantes.

  • Le début et la fin de la plage d’adresses
  • Le type : Private, Mapped, Image, etc.
  • L’état Reserve/Commit
  • La protection : lecture, écriture, exécution, Copy-on-Write, etc.
  • La correspondance avec un fichier ou une section
  • Des attributs spéciaux tels que les pages de garde

On les tient par plage pour réduire le coût de gestion. 256 MiB représentent 65 536 pages de 4 KiB.

Plutôt que de construire dès le départ une structure de gestion complète pour chaque page, le VAD gère « cette plage contiguë est une seule réservation ». Ensuite, les pages se matérialisent au fur et à mesure des besoins.

4.1. Trouver un VAD ne garantit pas que le défaut puisse être résolu

L’explication « s’il est dans un VAD, le défaut se résout ; sinon, violation d’accès » est commode comme porte d’entrée, mais elle simplifie trop. Même quand un VAD est trouvé, un accès ordinaire ne peut pas continuer dans des cas tels que les suivants.

  • Seul Reserve a été fait, et la page visée n’est pas en Commit
  • La page est PAGE_NOACCESS
  • Une écriture a été faite sur une page en lecture seule
  • Une instruction a été exécutée depuis une page non exécutable
  • Une page de garde a été touchée pour la première fois
  • L’accès est sorti de la plage valide d’une section

À l’inverse, même si la PTE est invalide, si l’état logiciel du VAD et de la PTE montre que l’accès est légitime, on peut le résoudre en demand-zero, restauration Transition, page-in ou CoW. Pour être précis, on évalue ensemble le VAD, la PTE, les attributs de protection et le type d’accès.

5. Tables de pages et TLB

Le pointeur que détient l’application est une adresse virtuelle. Pour que le processeur accède à la RAM, il doit traduire le numéro de page virtuelle en numéro de page physique. Cette table de traduction hiérarchique est la table de pages, et l’entrée terminale est la PTE (Page Table Entry).

Une PTE valide porte, conceptuellement, le PFN, la protection lecture/écriture/exécution, l’autorisation en mode utilisateur, Accessed/Dirty, et d’autres informations. La disposition réelle des bits dépend du processeur et de la version de Windows.

Cela dit, parcourir la table de pages à chaque accès serait trop lent, donc le processeur met en cache les traductions récentes dans le TLB (Translation Lookaside Buffer). La traduction d’adresse avance dans l’ordre suivant.

  1. Si le TLB a une traduction et que l’accès est conforme à sa protection, ce résultat est utilisé.
  2. Si le TLB n’a pas de traduction, le processeur parcourt la table de pages.
  3. S’il existe une PTE valide et que l’accès est aussi conforme à la protection, la traduction est enregistrée dans le TLB et l’exécution continue.
  4. S’il n’y a pas de traduction valide, ou s’il y a une violation de protection, on entre par le point d’entrée du défaut de page. La vérification de protection a lieu aussi lorsque la traduction vient du TLB.

Le point facile à confondre ici, c’est qu’un échec TLB et un défaut de page sont distincts.

Situation Ce qui se produit ensuite
Le TLB a une traduction et l’accès est conforme à la protection Continuer avec la traduction en cache
Le TLB n’a pas de traduction, mais il existe une PTE valide et l’accès est conforme à la protection Obtenir la traduction par un parcours de table de pages et continuer
Il n’y a pas de traduction valide, ou l’accès viole la protection Entrer par le point d’entrée du défaut de page

La vérification de protection fonctionne aussi sur un succès TLB. Une écriture sur une page en lecture seule, ou l’exécution d’une instruction sur une page non exécutable, provoque un défaut même si la traduction est déjà en cache. C’est aussi pour cela qu’une écriture sur une page CoW peut provoquer un défaut.

Flux de traduction d'adresse et point d'entrée du défaut de pageMême si le TLB a une traduction, un accès non conforme à la protection entre par le point d'entrée du défaut de page. Si le TLB n'a pas de traduction, on parcourt la table de pages ; une PTE valide dont la protection est aussi conforme est enregistrée dans le TLB et l'exécution continue ; une traduction invalide ou une violation de protection entre par le point d'entrée du défaut de pageOuiConformeViolation de protectionNonOuiInvalide ou violation de protectionAccès mémoireLe TLB a-t-il une traduction ?L'accès est-il conforme à la protection ?Continuer avec cette traductionVers le point d'entrée du défaut de pageParcours de la table de pagesPTE valide et protection aussi conforme ?Enregistrer dans le TLB et continuer (pas de défaut)

Figure 3 : Un échec TLB peut se résoudre par un parcours de table de pages. On entre dans un défaut de page quand la traduction est invalide ou qu’il y a une violation de protection, et une violation de protection peut aussi se produire sur un succès TLB.

5.1. Une PTE invalide n’est pas une simple case vide

Dire qu’une PTE est invalide ne signifie pas que son contenu est vide. À partir de l’état logiciel d’une PTE invalide, Windows distingue par exemple les cas suivants.

  • Une page demand-zero jamais encore matérialisée
  • Une page Transition qui reste en RAM
  • Une page partagée qui référence une Prototype PTE
  • Une page privée enregistrée dans le fichier d’échange
  • Une violation de protection ou une région invalide

Le travail du processeur s’arrête à juger « ce n’est pas une traduction valide ordinaire » et à passer la main au noyau ; c’est le gestionnaire de mémoire qui donne le sens ensuite.

6. Un défaut de page de bout en bout

Suivons une première écriture sur une page privée en Commit, en six étapes.

  1. Le processeur tente d’écrire.
    Il examine le TLB et la table de pages, mais la PTE cible n’a pas de PFN valide.
  2. Le processeur déclenche un défaut de page.
    Il transmet au noyau l’adresse virtuelle en défaut, le type lecture/écriture/exécution, utilisateur/noyau, et s’il s’agit d’une traduction absente ou d’une violation de protection.
  3. Le gestionnaire de mémoire examine le VAD et la PTE.
    Il décide si la page est en Commit, si l’accès est conforme à la protection, et laquelle des voies s’applique : demand-zero, Transition, partagée, page-in, CoW ou exception.
  4. S’il s’agit d’un demand-zero, une page physique mise à zéro est obtenue.
    Une page nouvellement remise doit être à zéro, pour ne pas fuiter les données d’un autre processus.
  5. La PTE et les informations de gestion PFN sont mises à jour.
    Le PFN et la protection sont écrits dans la PTE, la page physique passe Active, et elle est ajoutée au Working Set du processus.
  6. L’instruction qui a échoué est réexécutée.
    Comme le défaut s’est résolu normalement, aucune exception en mode utilisateur n’est livrée, et l’application continue comme s’il s’agissait d’une affectation ordinaire.

Les événements de défaut de page d’ETW enregistrent aussi Transition, Demand Zero, Copy-on-Write, Guard Page, Hard Page Fault et Access Violation comme des types distincts.4

Autrement dit, un défaut de page n’est pas, dès le départ, un mot qui signifie « anomalie ». C’est le point d’entrée commun pour demander à l’OS de décider, lorsque le processeur n’a pas pu traduire par le chemin ordinaire.

Où un défaut de page se résoutLe gestionnaire de mémoire évalue le VAD, la PTE, les attributs de protection et le type d'accès, et oriente vers un demand-zero, une reconnexion d'une page encore en RAM, un défaut matériel depuis un backing store, une copie sur écriture, une notification de page de garde, ou une exceptionPremier accèsEncore en RAMLecture disque requiseÉcriture CoWPage de gardeNon résolubleSurvenue du défaut de pageÉvaluation VAD, PTE, protection, typeDemand-zero (logiciel)Reconnexion depuis Standby, etc. (logiciel)Défaut matériel (E/S disque)Copie et remplacement de la PTELevée de la garde et notificationException (0xC0000005, etc.)

Figure 4 : Les défauts qui entrent par le même point d’entrée se séparent en six issues selon le résultat de l’évaluation. Le détail des pages de garde est traité à la section 9.

7. Demand-zero — un défaut logiciel qui ne lit pas le disque

Le demand-zero est le défaut logiciel représentatif qui se produit lorsqu’on touche pour la première fois une page privée en Commit. La documentation Microsoft sur le Working Set cite aussi, comme exemple de défaut logiciel, le cas où « le processus référence pour la première fois une page virtuelle allouée ».5

Le demand-zero a les caractéristiques suivantes.

  • Il n’est pas nécessaire de lire des données d’origine depuis le disque
  • Le contenu initial est zéro
  • Une page physique disponible est liée
  • Le Working Set et le Page Fault Count cumulé augmentent
  • Ce traitement seul n’augmente pas Memory\\Pages Input/sec

C’est pourquoi un pic de Page Faults/sec juste après le démarrage ne signifie pas, à lui seul, que le stockage est saturé.

7.1. L’allocation différée échange de la RAM contre le coût du premier accès

Si vous committez 256 MiB mais n’en utilisez réellement que 8 MiB, l’allocation différée, qui évite de placer les 248 MiB restants en RAM, est raisonnable. En échange, le premier accès porte le coût du traitement du défaut.

Pour un traitement sensible à la latence, on choisit parfois de « prefault » en touchant chaque page avant de commencer. Ce n’est toutefois pas une optimisation gratuite. C’est le choix de terminer d’avance le traitement du premier accès, en augmentant d’avance aussi la quantité résidente en RAM.

8. Défauts logiciels et défauts matériels

La ligne de partage est la nécessité ou non d’une E/S de lecture depuis un backing store. Le nombre de défauts de page, à lui seul, ne révèle pas cette différence.

8.1. Défauts logiciels

Un défaut logiciel est un défaut que l’on peut résoudre sans E/S de lecture vers un backing store. Voici des exemples représentatifs.

  • Demand-zero
  • Reconnexion d’une page qui reste en Standby/Transition
  • Connexion d’une page partagée présente dans le Working Set d’un autre processus
  • Connexion d’une page déjà préchargée
  • Copy-on-Write dont la page d’origine est résidente

Il y a un coût processeur pour la transition noyau, les verrous, les mises à jour PTE/PFN, la cohérence du TLB, etc., mais il n’y a pas d’attente de stockage.5

8.2. Défauts matériels

En revanche, lorsque la page nécessaire n’est nulle part en RAM et doit être lue depuis un backing store, c’est un défaut matériel. La source de lecture n’est alors pas seulement le fichier d’échange.

  • Une page privée évacuée vers le fichier d’échange
  • Un fichier mappé en mémoire
  • Une image EXE ou DLL
  • Un fichier de données référencé par le cache de fichiers

Les événements HardFault d’ETW incluent FileObject, ReadOffset et ByteCount, ce qui permet de suivre la source de lecture réelle.6

Donc Hard Fault = lecture de pagefile.sys est faux.

Quand une lecture de backing store devient nécessaire, la demande entre dans la pile d’E/S de Windows. Le flux des IRP et de l’émission/complétion est traité dans « Les profondeurs de l’E/S Windows (partie 1) », et la jonction avec le cache de fichiers dans « Les profondeurs de l’E/S Windows (partie 4) ». Si la page est en RAM, le gestionnaire de mémoire peut revenir seul ; sinon, il émet une E/S et fait attendre le thread en défaut jusqu’à la complétion.

9. Un défaut non résoluble devient une exception

Un défaut qui, même après examen du VAD et de la PTE, ne peut pas être résolu comme une allocation légitime, un page-in ou un CoW, est livré en mode utilisateur sous forme d’exception.

9.1. Les cas qui deviennent une violation d’accès

Le cas représentatif est STATUS_ACCESS_VIOLATION, code d’exception 0xC0000005. Il se produit sur une lecture, une écriture ou une exécution d’une adresse invalide ; le premier paramètre d’exception indique le type d’accès et le second l’adresse en infraction.7

Les schémas typiques d’occurrence sont les suivants.

  • Lire NULL, une adresse déjà libérée, ou une adresse hors tableau
  • Écrire sur une page en lecture seule
  • Exécuter une instruction depuis une page rendue non exécutable par DEP/NX
  • Toucher une plage en Reserve qui n’est pas en Commit

9.2. Une page de garde sert de notification unique

PAGE_GUARD a un sens un peu différent. C’est un mécanisme qui notifie un accès une seule fois, en levant STATUS_GUARD_PAGE_VIOLATION, et il sert notamment à l’extension de pile.8

L’allocation différée normale, le page-in, le CoW, la notification de garde et la violation d’accès finale se rassemblent, du point de vue du processeur, au même point d’entrée de défaut de page. Ce qui décide l’issue, c’est la combinaison du VAD, de la PTE, des attributs de protection et du type d’accès.

10. Le vérifier de vos propres yeux

Le but de l’expérience est d’observer séparément l’étape où le Commit augmente et celle où le Working Set augmente.

Le programme C++ suivant réserve 256 MiB, les committe, écrit un octet dans chaque page, puis les libère. Il attend la touche Entrée à chaque étape, afin que vous puissiez y vérifier les valeurs dans VMMap et PerfMon.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>

#include <cstdio>
#include <cstdlib>

#pragma comment(lib, "Psapi.lib")

constexpr SIZE_T kSize = 256ull * 1024 * 1024;

void PrintMemory(const char* stage)
{
    PROCESS_MEMORY_COUNTERS_EX c{};
    c.cb = sizeof(c);
    if (!GetProcessMemoryInfo(
            GetCurrentProcess(),
            reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&c),
            sizeof(c))) {
        std::printf("GetProcessMemoryInfo failed: %lu\n", GetLastError());
        return;
    }

    std::printf(
        "%-10s WS=%zu MiB  Private=%zu MiB  Faults=%lu\n",
        stage,
        c.WorkingSetSize / 1024 / 1024,
        c.PrivateUsage / 1024 / 1024,
        c.PageFaultCount);
}

void Pause(const char* message)
{
    PrintMemory(message);
    std::puts("Press Enter...");
    (void)std::getchar();
}

int main()
{
    SYSTEM_INFO si{};
    GetSystemInfo(&si);
    std::printf("PID=%lu, page=%lu bytes\n",
                GetCurrentProcessId(), si.dwPageSize);

    void* base = VirtualAlloc(nullptr, kSize, MEM_RESERVE, PAGE_NOACCESS);
    if (!base) {
        std::fprintf(stderr, "Reserve failed: %lu\n", GetLastError());
        return EXIT_FAILURE;
    }
    Pause("reserved");

    if (!VirtualAlloc(base, kSize, MEM_COMMIT, PAGE_READWRITE)) {
        std::fprintf(stderr, "Commit failed: %lu\n", GetLastError());
        VirtualFree(base, 0, MEM_RELEASE);
        return EXIT_FAILURE;
    }
    Pause("committed");

    auto* bytes = static_cast<volatile unsigned char*>(base);
    for (SIZE_T offset = 0; offset < kSize; offset += si.dwPageSize) {
        bytes[offset] = 1;
    }
    Pause("touched");

    if (!VirtualFree(base, 0, MEM_RELEASE)) {
        std::fprintf(stderr, "Release failed: %lu\n", GetLastError());
        return EXIT_FAILURE;
    }
    Pause("released");
}

Depuis l’invite de commandes x64 Native Tools de Visual Studio, vous pouvez compiler avec la commande suivante.

cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp

10.1. Ce qu’il faut regarder dans VMMap

VMMap est un outil qui affiche, par type, la mémoire virtuelle réservée, le Commit, le Working Set, Private et Shareable.9 Les changements attendus à chaque étape sont les suivants.

Étape Changement attendu
Reserve La Size de Address Space augmente, mais Commit/WS n’augmentent pas d’autant
Commit Le Commit Private augmente d’environ 256 MiB
Touch Le Working Set et le Private WS augmentent fortement, et le Fault Count aussi
Release La plage cible disparaît, et le Commit et le WS baissent

Regarder le changement entre étapes, pas la coïncidence des chiffres

Les chiffres réels varient avec le runtime, les produits de sécurité, la pression mémoire et le moment de l’observation. Ce n’est pas de savoir s’ils tombent pile sur 256 MiB qui compte, mais dans quel sens ils ont bougé d’une étape à l’autre.

10.2. Séparer logiciel et matériel dans PerfMon

Dans PerfMon, placez les compteurs suivants sur le même axe temporel.

  • Process(<cible>)\\Page Faults/sec
  • Memory\\Pages Input/sec
  • Memory\\Page Reads/sec
  • Memory\\Available MBytes
  • Process(<cible>)\\Working Set - Private
  • Process(<cible>)\\Private Bytes

Process\\Page Faults/sec inclut à la fois les défauts logiciels et les défauts matériels. Memory\\Pages Input/sec, lui, est le nombre de pages lues depuis le disque pour résoudre des défauts matériels.10

À l’étape Touch de ce programme, Page Faults/sec devrait s’envoler sans que Pages Input/sec n’augmente beaucoup. Les pages nouvellement commitées se matérialisent par demand-zero, donc il n’est pas nécessaire de lire des données d’origine depuis le disque.

Confirmer aussi par PID les processus homonymes

Lorsque plusieurs processus portent le même nom, les numéros PerfMon du type process#1 peuvent changer à un redémarrage. Croisez avec un compteur qui affiche le PID, ou identifiez le processus par PID avec Process V2 ou ETW/WPA.

11. Trois lectures erronées à éviter sur le terrain

11.1. « Le Commit a augmenté, donc c’est une fuite de RAM »

Le Commit est la quantité promise à conserver, et les pages non touchées peuvent ne pas être résidentes en RAM. Pour juger une fuite, on regarde l’évolution temporelle des Private Bytes, la ventilation des allocations, et si la valeur revient à la ligne de base après le traitement.

11.2. « Page Faults/sec est élevé, donc le disque est lent »

Les défauts logiciels n’entraînent pas d’E/S disque. Séparez Page Faults/sec, Pages Input/sec et l’attente de stockage et, si besoin, suivez jusqu’au fichier source et à la pile avec les événements HardFault d’ETW.

11.3. « Vider le Working Set corrigera la fuite »

Retirer des pages du Working Set ne libère ni le Commit ni la propriété. Les pages passent en Standby ou Modified et reviendront plus tard par un défaut. Pour corriger une fuite, l’allocateur doit faire un VirtualFree, une libération de tas, une destruction d’objet, ou l’équivalent.

Où va ensuite cette page physique retirée, c’est ce que suit la partie 2.

12. Synthèse

  • MEM_RESERVE réserve une plage d’adresses virtuelles, mais n’attribue aucune zone physique en RAM ni dans le fichier d’échange.1
  • MEM_COMMIT consomme du Commit et garantit que le contenu pourra être conservé plus tard, mais une page physique ordinaire n’est pas attribuée avant le premier accès.12
  • Le VAD est le registre des plages, la PTE celui des pages virtuelles, et la base PFN celui des pages physiques.
  • Un échec TLB n’est pas un défaut de page. S’il existe une PTE valide, un parcours de table de pages suffit à le résoudre.
  • Le demand-zero, la restauration Transition et la connexion d’une page partagée sont des défauts logiciels résolubles sans E/S disque.5
  • S’il faut lire depuis le fichier d’échange, une DLL, un EXE ou un fichier mappé, c’est un défaut matériel.6
  • Si l’examen du VAD, de la PTE et des attributs de protection ne peut pas résoudre le défaut, il devient une exception telle que 0xC0000005.7
  • Pour juger la performance, ne vous fiez pas au seul Page Faults/sec : regardez Pages Input/sec, Available, Working Set, Private Bytes et l’attente de stockage sur le même axe temporel.

La suite est la partie 2, « La vie d’une page physique : cinq listes et la vérité sur le fichier d’échange ».

Après avoir transformé la promesse de Commit en page physique, nous suivrons, via la base PFN et les listes de pages, où va cette page une fois sortie du Working Set.

Articles connexes

Domaines de conseil associés

Chez KomuraSoft, nous prenons en charge l’investigation de l’usage mémoire des applications Windows, les violations d’accès, les lenteurs au démarrage, la pagination et l’analyse des anomalies du code natif.

Références

  1. Microsoft Learn, VirtualAlloc function. Sur le fait que MEM_RESERVE réserve une plage d’adresses virtuelles sans attribuer de stockage physique, que MEM_COMMIT impute une charge de commit sur la mémoire et le fichier d’échange du système, que le contenu initial d’une page en Commit est zéro, et que la page physique réelle n’est pas attribuée tant qu’elle n’est pas accédée. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8

  2. Microsoft Learn, PERFORMANCE_INFORMATION structure. Sur le fait que CommitTotal est le nombre actuel de pages en Commit du système, et que CommitLimit est le plafond que l’on peut committer sans étendre le fichier d’échange. ↩ ↩2 ↩3

  3. Microsoft Learn, !vad (WinDbg). Sur le fait que !vad affiche l’arbre des VAD et permet d’inspecter le VPN de début et de fin, le Commit, Mapped/Private, les attributs de protection, la Control Area, et d’autres champs. ↩

  4. Microsoft Learn, PageFault_TypeGroup1 class. Sur le fait qu’ETW distingue et enregistre Transition Fault, Demand Zero Fault, Copy-on-Write, Guard Page Fault, Hard Page Fault et Access Violation. ↩

  5. Microsoft Learn, Working Set. Sur le fait qu’un défaut logiciel peut se résoudre sans accès à un backing store, et qu’il se produit depuis le Working Set d’un autre processus, Transition, un demand-zero de première référence, etc. ↩ ↩2 ↩3

  6. Microsoft Learn, PageFault_HardFault class. Sur le fait qu’un événement HardFault inclut FileObject, ReadOffset, ByteCount, VirtualAddress et un identifiant de thread, ce qui permet de suivre la source de lecture. ↩ ↩2

  7. Microsoft Learn, Access Violation C0000005. Sur le fait que 0xC0000005 se produit sur une lecture, une écriture ou une exécution d’une adresse mémoire invalide, et que les paramètres d’exception indiquent le type d’accès et l’adresse en infraction. ↩ ↩2

  8. Microsoft Learn, Creating Guard Pages. Sur le fait que PAGE_GUARD fournit une notification unique d’accès à une page et lève STATUS_GUARD_PAGE_VIOLATION. ↩

  9. Microsoft Learn, VMMap - Sysinternals. Sur le fait que VMMap décompose la mémoire virtuelle en Commit par type et affiche le Working Set de chaque type ainsi qu’une carte d’adresses détaillée. ↩

  10. Microsoft Learn, Performance Analysis of Logs (PAL) Tool. Sur le fait que Memory\\Pages Input/sec est le nombre de pages lues depuis le disque pour résoudre un défaut de page matériel. ↩

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Passer MEM_COMMIT à VirtualAlloc alloue-t-il de la RAM à cet instant ?
Pour la mémoire privée ordinaire, le Commit consomme la marge de commit du système, mais les pages physiques correspondantes ne sont pas attribuées avant le premier accès. Une page d'abord touchée par une écriture obtient sa page physique pendant le traitement d'un défaut demand-zero.
Un défaut de page signifie-t-il une anomalie ou un problème de performance ?
Non. Les défauts logiciels sans E/S disque, comme un demand-zero ou le retour d'une page depuis Standby, font partie du fonctionnement normal. Pour juger la performance, ne vous fiez pas au seul Page Faults/sec : regardez aussi Pages Input/sec, l'attente de stockage et Available MBytes.
Un échec TLB et un défaut de page sont-ils la même chose ?
Ce sont deux choses distinctes. Même si le TLB n'a pas de traduction, si la PTE de la table de pages est valide, le processeur se contente de parcourir la table et de réenregistrer la traduction. On n'entre dans le défaut de page que si la PTE est invalide ou s'il y a une violation de protection.
Si la plage d'adresses figure dans un VAD, une violation d'accès est-elle impossible ?
Pas nécessairement. Outre l'existence d'un VAD, le gestionnaire de mémoire évalue Reserve ou Commit, les attributs de protection lecture/écriture/exécution, les pages de garde, l'état de la PTE, et d'autres critères. Si le défaut ne peut pas être résolu, il devient une exception telle que 0xC0000005.
Un Page Faults/sec élevé signifie-t-il que le système manque de RAM ?
On ne peut pas le dire à partir de cela seul. Page Faults/sec inclut aussi un grand nombre de défauts logiciels. Il faut le corréler sur le même axe temporel avec Memory\Pages Input/sec, Memory\Page Reads/sec, Available MBytes et le temps d'attente disque.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog