Interioridades de la memoria de Windows (1.ª parte) — El instante en que una dirección virtual se convierte en RAM física: el ciclo completo de un fallo de página
· Actualizado el: · Go Komura · Windows, Gestión de memoria, VirtualAlloc, Fallo de página, VAD, Monitoreo de rendimiento
Al pasar MEM_COMMIT a VirtualAlloc, el Commit aumenta en ese mismo instante. Sin embargo, el Working Set no aumenta necesariamente en la misma medida. Entonces, ¿dónde está la memoria que supuestamente se reservó?
La respuesta es que «la mayoría de las páginas todavía no tienen RAM física correspondiente». Windows retrasa la asignación de páginas físicas hasta que la aplicación realmente toca la página. Cuando el primer acceso hace que la CPU genere un fallo de página, el administrador de memoria examina el VAD, la PTE, los atributos de protección y el backing store, y vincula la RAM página por página cuando es necesario.1
En este artículo seguimos el camino que recorre ese «primer byte tocado» hasta llegar a la RAM física. Si primero quiere aclarar el significado de cifras como Working Set o Commit, consulte el artículo introductorio «Qué representa el “uso de memoria” de Windows — cómo leer correctamente Working Set, Private Bytes, Commit y el archivo de paginación». Esta serie no vuelve a definir la terminología usada allí, sino que profundiza desde el lado mecánico en «por qué esa cifra llega a ser la que es».
Las tres partes de «Interioridades de la memoria de Windows»
- 1.ª parte (este artículo): direcciones virtuales y fallos de página
Seguimos cuándo una región reservada conVirtualAllocobtiene RAM física. - 2.ª parte: el ciclo de vida de una página física
Seguimos cómo una página que sale del Working Set se mueve entre Modified, Standby, Free y Zeroed. - 3.ª parte: objetos de sección y copy-on-write
Seguimos por qué los DLL, las asignaciones de archivos y la memoria compartida pueden compartir páginas físicas.
La 1.ª parte responde a una sola pregunta.
¿En qué instante una dirección virtual ya en Commit se convierte en RAM física?
El público objetivo son desarrolladores y responsables de operación que quieren entender, desde el mecanismo mismo, el uso de memoria de las aplicaciones Windows, los fallos de página justo después del arranque, 0xC0000005 y las cifras de VMMap o PerfMon. El entorno previsto es Windows 10/11 o una versión actual de Windows Server, y los conocimientos previos necesarios son punteros y los fundamentos de VirtualAlloc; no hace falta experiencia con la disposición de bits de la tabla de páginas ni con el depurador de kernel. El nivel es intermedio. Se mencionan nombres de estructuras internas, pero no se asume ningún diseño no documentado que dependa de una compilación concreta de Windows.
1. La conclusión primero
El flujo habitual de la memoria privada se puede resumir en una sola línea.
Reserve reserva un rango de direcciones virtuales, Commit cobra una cuota de compromiso que garantiza un destino de almacenamiento futuro, y el fallo de página del primer acceso asigna la página física.
Es decir, MEM_COMMIT no es una orden que diga «reserve RAM ahora mismo». La propia documentación de VirtualAlloc de Microsoft garantiza que el contenido inicial de una página en Commit es cero, pero explica que la página física real no se asigna hasta que se accede a la dirección virtual.1
Aunque tampoco es del todo exacto decir que «Reserve/Commit solo escriben en el VAD». En realidad, Reserve crea un VAD que representa principalmente el rango de direcciones virtuales y sus atributos, y Commit incrementa el Commit Total del sistema y registra el estado de compromiso del rango. Los niveles intermedios de la tabla de páginas y cada PTE individual se construyen de forma diferida cuando se necesitan, y el enlace final con la RAM física ocurre normalmente en el primer acceso.
Commit no es una promesa vacía: es la promesa de todo el sistema de que en el futuro podrá conservar ese contenido en RAM o en el backing store adecuado. La puerta de entrada que materializa esa promesa página por página es el fallo de página.
flowchart TB
accTitle: Qué sucede en Reserve, Commit y el primer acceso
accDescr: MEM_RESERVE registra el rango y los atributos en el VAD, MEM_COMMIT consume el Commit Total para prometer el almacenamiento, y el fallo de página del primer acceso asigna la página física y la añade al Working Set
reserve["1. MEM_RESERVE"] --> commit["2. MEM_COMMIT"]
commit --> touch["3. Primer acceso(Touch)"]
reserve -.-> vad["Registra el rango y los atributos en el VAD"]
commit -.-> charge["Consume el Commit Total(aún sin página física)"]
touch --> fault["Fallo de página"]
fault --> zero["Vincula una página física puesta a cero a la PTE"]
zero --> ws["La añade al Working Set y reejecuta la instrucción"]
Figura 1: Reserve, Commit y Touch son sucesos distintos. La RAM física se vincula recién en ese último primer acceso.
2. Los tres registros que siguen la página virtual
Para entender el flujo desde la dirección virtual hasta la RAM física, hay que distinguir los tres tipos de registros que mantiene Windows.
| Registro | Unidad | Función |
|---|---|---|
| VAD | Rango de direcciones virtuales | Gestiona de qué es la región, Reserve/Commit, la protección y la correspondencia con la sección |
| Tabla de páginas / PTE | Página virtual | Representa la traducción a la página física actual o el estado sin materializar |
| Base de datos PFN | Página física | Rastrea la propiedad, las referencias y el estado de cada página de RAM |
El VAD contiene información sobre el rango, la PTE sobre la página virtual y la base de datos PFN sobre la página física. El controlador de fallos de página cruza esta información para decidir si el acceso puede continuar.
flowchart TB
accTitle: Los tres registros entre la dirección virtual y la RAM física
accDescr: La dirección virtual la gestiona el VAD por rango y la PTE por página virtual, y la página física a la que apunta la PTE la rastrea la base de datos PFN por página física
va["Dirección virtual"] --> vad["VAD(registro de rangos)"]
va --> pte["PTE(registro de páginas virtuales)"]
vad -.->|Evalúa Reserve/Commit y protección| pte
pte -->|Traducción válida| pfn["Base de datos PFN(registro de páginas físicas)"]
pfn --> ram["Página de RAM física"]
Figura 2: Tres registros de distinta granularidad. El procesamiento del fallo cruza el VAD y la PTE, y refleja el resultado en el lado del PFN.
Los protagonistas de este artículo son el VAD y la PTE. La base de datos PFN la abordaremos en la 2.ª parte desde el lado de la página física.
3. Reserve, Commit y Touch son sucesos distintos
3.1. Reserve — reservar la dirección
Primero, reservemos un rango contiguo de direcciones virtuales de 256 MiB.
void* base = VirtualAlloc(
nullptr,
256ull * 1024 * 1024,
MEM_RESERVE,
PAGE_NOACCESS);
En este punto lo único que ha ocurrido es que se ha reservado una dirección en el espacio virtual del proceso para impedir que este rango se use en otra asignación. MEM_RESERVE no asigna almacenamiento físico ni en la RAM ni en el archivo de paginación.1
Como los procesos de 64 bits disponen de un espacio virtual enorme, resulta práctico diseñar el sistema reservando primero un rango grande con Reserve y haciendo Commit más adelante solo de la parte que realmente se necesita.
3.2. Commit — prometer que se podrá guardar
A continuación, hagamos Commit del rango ya reservado.
void* committed = VirtualAlloc(
base,
256ull * 1024 * 1024,
MEM_COMMIT,
PAGE_READWRITE);
Si tiene éxito, aumentan el Commit Total del sistema y la cantidad prometida que normalmente se refleja en Private Bytes del proceso. Aun así, no aparecen de golpe 256 MiB de páginas físicas. Las páginas normales permanecen sin asignación física hasta el primer acceso.12
Entonces, ¿qué sentido tiene Commit? Que si el sistema no puede asumir la promesa, puede devolver el fallo en el momento del Commit en lugar de en mitad del uso de la memoria.
3.3. Touch — cuando se necesita la página física
Por último, con la siguiente asignación escribimos por primera vez en la página inicial.
static_cast<unsigned char*>(base)[0] = 1;
La CPU intenta traducir la dirección virtual a una dirección física, pero la PTE todavía no tiene una traducción válida a una página física. Aquí se produce el fallo de página.
El administrador de memoria, que recibe el control, determina que se trata de «un primer acceso a una página privada en Commit y con permiso de escritura», reserva una página física puesta a cero, la vincula a la PTE y la añade al Working Set. A continuación, hace que se vuelva a ejecutar la instrucción de escritura que había fallado.
Desde la aplicación solo se ve una simple asignación, pero internamente se ejecuta un proceso que entra en el kernel en mitad de la asignación, asigna la página física y vuelve a la misma instrucción.
4. VAD — el registro de rangos del espacio virtual
VAD son las siglas de Virtual Address Descriptor, y Windows gestiona los rangos de direcciones en uso de un proceso como un árbol de VAD. Con el comando !vad de WinDbg se pueden consultar la VPN inicial y final, el Commit, los atributos de protección, si es Private o Mapped, el Control Area, entre otros datos.3
La información representativa que contiene un VAD es la siguiente.
- El inicio y el final del rango de direcciones
- El tipo: Private, Mapped, Image, etc.
- El estado de Reserve/Commit
- La protección: lectura, escritura, ejecución, copy-on-write, etc.
- La correspondencia con un archivo o una sección
- Atributos especiales como las páginas de guarda
La razón de gestionar por rangos es la eficiencia. 256 MiB equivalen a 65 536 páginas de 4 KiB. En lugar de crear desde el principio una estructura de gestión completa para todas las páginas, resulta menos costoso mantener en el VAD la información de que «este rango contiguo es una sola reserva» e ir concretando las páginas a medida que se necesitan.
4.1. Encontrar el VAD no garantiza poder recuperarse
La explicación de que «si está en el VAD, el fallo se resuelve, y si no está, hay una infracción de acceso» es útil como punto de partida, pero es una simplificación excesiva. Aunque se encuentre el VAD, en casos como los siguientes no se puede continuar con un acceso normal.
- Solo está en Reserve y la página en cuestión no está en Commit
- Es
PAGE_NOACCESS - Se escribió en una página de solo lectura
- Se ejecutó una instrucción desde una página no ejecutable
- Se tocó una página de guarda por primera vez
- Se accedió fuera del rango válido de la sección
En cambio, aunque la PTE sea inválida, si el estado de software del VAD y de la PTE indican que el acceso es legítimo, puede resolverse como demand zero, recuperación desde Transition, paginación de entrada o CoW. Dicho con precisión, la respuesta es que se evalúan en conjunto el VAD, la PTE, los atributos de protección y el tipo de acceso.
5. La tabla de páginas y el TLB
Los punteros que maneja una aplicación son direcciones virtuales. Para que la CPU acceda a la RAM, debe traducir el número de página virtual a un número de página física. Esa tabla jerárquica de traducción es la tabla de páginas, y su entrada final es la PTE (Page Table Entry).
Conceptualmente, una PTE válida contiene el PFN, la protección de lectura/escritura/ejecución, si permite modo usuario, los bits Accessed/Dirty, entre otra información. La disposición real de los bits depende de la CPU y de la versión de Windows.
Ahora bien, recorrer la tabla de páginas cada vez sería demasiado lento, así que la CPU almacena en caché las traducciones recientes en el TLB (Translation Lookaside Buffer). La traducción de direcciones avanza en este orden.
- Si el TLB tiene la traducción y el acceso cumple su protección, se usa ese resultado.
- Si el TLB no tiene la traducción, la CPU recorre la tabla de páginas.
- Si hay una PTE válida y también cumple la protección, se registra en el TLB y se continúa.
- Si no hay una traducción válida o hay una violación de protección, se pasa a la entrada del fallo de página. La comprobación de protección también se realiza cuando la traducción viene del TLB.
Como se ve en este flujo, un fallo de TLB y un fallo de página son cosas distintas. Si simplemente no hay traducción en el TLB pero la PTE es válida, el proceso termina solo con el recorrido de la tabla de páginas. Al contrario, incluso con una traducción en el TLB, una violación de protección como escribir en una página de solo lectura o ejecutar una instrucción en una página no ejecutable avanza hacia la entrada del fallo de página. Por eso una escritura en una página CoW puede provocar un fallo aunque la traducción ya esté en caché.
flowchart TB
accTitle: Flujo de la traducción de direcciones y la entrada al fallo de página
accDescr: Aunque el TLB tenga la traducción, si no cumple la protección se entra en el fallo de página. Si el TLB no la tiene, se recorre la tabla de páginas; si la PTE es válida y cumple la protección, se registra en el TLB y continúa; si es inválida o hay violación de protección, se entra en el fallo de página
access["Acceso a memoria"] --> tlb{"¿El TLB tiene la traducción?"}
tlb -->|Sí| perm{"¿Cumple la protección?"}
perm -->|Cumple| go["Continúa con esa traducción"]
perm -->|Violación de protección| entry["Hacia la entrada del fallo de página"]
tlb -->|No| walk["Recorrido de la tabla de páginas"]
walk --> valid{"¿PTE válida y protección conforme?"}
valid -->|Sí| register["Se registra en el TLB y continúa(sin fallo)"]
valid -->|Inválida o violación de protección| entry
Figura 3: Un fallo de TLB se puede resolver con el recorrido de la tabla de páginas. Se avanza hacia el fallo de página cuando la traducción es inválida o hay una violación de protección, y esta última también puede ocurrir con un acierto de TLB.
5.1. Una PTE inválida no es simplemente un espacio vacío
Aunque se hable de una PTE inválida, eso no significa que su contenido esté vacío. Windows distingue, a partir del estado de software de una PTE inválida, casos como los siguientes.
- Una página de demand zero que nunca se ha materializado
- Una página en Transition que sigue en RAM
- Una página compartida que referencia una Prototype PTE
- Una página privada guardada en el archivo de paginación
- Una violación de protección o una región inválida
El trabajo de la CPU llega hasta determinar que «no es una traducción válida normal» y entregarlo al kernel; interpretar el significado a partir de ahí corresponde al administrador de memoria.
6. El ciclo completo de un fallo de página
Sigamos en seis etapas una primera escritura en una página privada en Commit.
- La CPU intenta escribir.
Consulta el TLB y la tabla de páginas, pero la PTE correspondiente no tiene un PFN válido. - La CPU genera un fallo de página.
Entrega al kernel la dirección virtual que falló, el tipo de acceso (lectura, escritura o ejecución), si es modo usuario o kernel, y si es por ausencia de traducción o por violación de protección. - El administrador de memoria examina el VAD y la PTE.
Decide si está en Commit, si cumple la protección y si se trata de demand zero, Transition, compartida, paginación de entrada, CoW o una excepción. - Si es demand zero, obtiene una página física puesta a cero.
Para no filtrar datos de otros procesos, cualquier página nueva que se entregue debe estar en cero. - Actualiza la PTE y la información de gestión del PFN.
Configura el PFN y la protección en la PTE, marca la página física como Active y la añade al Working Set del proceso. - Reejecuta la instrucción que había fallado.
Como se resolvió con normalidad, no llega ninguna excepción en modo usuario y la aplicación continúa como si fuera una asignación normal.
Los eventos de fallo de página de ETW también registran Transition, Demand Zero, Copy-on-Write, Guard Page, Hard Page Fault y Access Violation como tipos distintos.4
Es decir, «fallo de página» no significa «anomalía» desde el principio. Es la entrada común mediante la cual, cuando la CPU no puede traducir por la vía normal, se le pide al sistema operativo que decida qué hacer.
flowchart LR
accTitle: Ramificación de las resoluciones del fallo de página
accDescr: El administrador de memoria evalúa el VAD, la PTE, los atributos de protección y el tipo de acceso, y deriva hacia demand zero, la reconexión de una página que sigue en RAM, un fallo duro desde el backing store, copy-on-write, la notificación de página de guarda o una excepción
faultIn["Se produce el fallo de página"] --> judge["Evalúa VAD, PTE, protección y tipo"]
judge -->|Primer acceso| dz["Demand zero(suave)"]
judge -->|Sigue en RAM| soft["Reconexión desde Standby, etc.(suave)"]
judge -->|Requiere leer disco| hard["Fallo duro(E/S de disco)"]
judge -->|Escritura CoW| cow["Copia y sustituye la PTE"]
judge -->|Página de guarda| guard["Retira la guarda y notifica"]
judge -->|Sin resolución| av["Excepción(0xC0000005, etc.)"]
Figura 4: Un fallo que entra por la misma puerta se ramifica, según el resultado del juicio, en seis desenlaces distintos. Los detalles de la página de guarda se tratan en el apartado 9.
7. Demand zero — el fallo suave que no lee el disco
Demand zero es el fallo suave representativo que ocurre al tocar por primera vez una página privada en Commit. La propia explicación de Microsoft sobre Working Set cita como ejemplo de fallo suave el caso en que «un proceso hace referencia por primera vez a una página virtual ya asignada».5
Demand zero tiene las siguientes características.
- No hace falta leer los datos originales del disco
- El contenido inicial es cero
- Vincula una página física disponible
- El Working Set y el Page Fault Count acumulado aumentan
- Si solo ocurre este proceso,
Memory\\Pages Input/secno aumenta
Por eso, aunque Page Faults/sec se dispare justo después del arranque, eso por sí solo no significa que el almacenamiento esté saturado.
Repasemos también las ventajas y desventajas de la asignación diferida. Si se hace Commit de 256 MiB pero en realidad solo se usan 8 MiB, es razonable que la asignación diferida no coloque en RAM los 248 MiB restantes. A cambio, el primer acceso carga el coste del procesamiento del fallo. En procesos con requisitos estrictos de latencia existe también el diseño de tocar cada página de antemano para provocar un prefallo (prefault) antes de empezar, pero esa es una decisión que intercambia esa ventaja por aumentar de antemano la cantidad de memoria residente en RAM.
8. Fallos suaves y fallos duros
8.1. Fallos suaves
Un fallo suave es aquel que se puede resolver sin una operación de E/S de lectura al backing store. Estos son los casos representativos.
- Demand zero
- La reconexión de una página que permanece en Standby/Transition
- La conexión de una página compartida presente en el Working Set de otro proceso
- La conexión de una página ya leída por adelantado (prefetch)
- Copy-on-write cuando la página original ya está residente
Conlleva costes de CPU como la transición al kernel, los bloqueos, la actualización de la PTE/PFN y la coherencia del TLB, pero no hay espera de almacenamiento.5
8.2. Fallos duros
En cambio, un fallo duro es cuando la página necesaria no está en ningún lugar de la RAM y hay que leerla del backing store. En este caso, el origen de la lectura no se limita al archivo de paginación.
- Una página privada expulsada al archivo de paginación
- Un archivo mapeado en memoria
- La imagen de un EXE o un DLL
- Un archivo de datos al que hace referencia la caché de archivos
El evento HardFault de ETW incluye FileObject, ReadOffset y ByteCount, lo que permite rastrear el origen real de la lectura.6
Por lo tanto, Hard Fault no equivale a “se leyó pagefile.sys”.
Cuando se necesita leer del backing store, la solicitud entra en la pila de E/S de Windows. El flujo de la IRP y su emisión y finalización se tratan en «Interioridades de la E/S de Windows (1.ª parte)», y su confluencia con la caché de archivos, en «Interioridades de la E/S de Windows (4.ª parte)». Si la página está en RAM, se puede volver solo con el administrador de memoria, pero si no lo está, hay que emitir la E/S y hacer esperar al hilo que provocó el fallo hasta que se complete.
9. Los fallos que no se pueden resolver se convierten en excepciones
Un fallo que, tras examinar el VAD y la PTE, no se puede resolver como una asignación legítima, una paginación de entrada o un CoW, llega al modo usuario como una excepción.
El caso representativo es STATUS_ACCESS_VIOLATION, con el código de excepción 0xC0000005. Se produce al leer, escribir o ejecutar una dirección inválida; el primer parámetro de la excepción indica el tipo de acceso y el segundo, la dirección que infringió la protección.7
Los patrones típicos en los que se produce son los siguientes.
- Leer una dirección NULL, ya liberada o fuera de un arreglo
- Escribir en una página de solo lectura
- Ejecutar una instrucción desde una página no ejecutable por DEP/NX
- Tocar un rango en Reserve que no está en Commit
Cabe señalar que PAGE_GUARD tiene un significado un poco distinto. Es un mecanismo que notifica el acceso una sola vez, genera STATUS_GUARD_PAGE_VIOLATION y se usa, por ejemplo, para la expansión de la pila.8
La asignación diferida normal, la paginación de entrada, el CoW, la notificación de guarda y, en última instancia, la infracción de acceso: vistos desde la CPU, todos convergen en la misma entrada del fallo de página. Lo que decide el desenlace es la combinación del VAD, la PTE, los atributos de protección y el tipo de acceso.
10. Compruébelo usted mismo
El flujo descrito hasta aquí se puede observar directamente. El siguiente programa en C++ reserva 256 MiB, hace Commit, escribe 1 byte en cada página y finalmente hace Release. En cada etapa espera a que se pulse Intro, de modo que puede observar los cambios con VMMap y 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");
}
Desde el x64 Native Tools Command Prompt de Visual Studio, se puede compilar con el siguiente comando.
cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp
10.1. Qué observar en VMMap
VMMap es una herramienta que muestra por categorías la memoria virtual reservada, el Commit, el Working Set, lo Private y lo Shareable.9 Los cambios esperados en cada etapa son los siguientes.
| Etapa | Cambio esperado |
|---|---|
| Reserve | El Size de Address Space aumenta, pero Commit/WS no aumenta en la misma medida |
| Commit | El Commit de Private aumenta en unos 256 MiB |
| Touch | El Working Set y el Private WS aumentan considerablemente, y el Fault Count también sube |
| Release | El rango desaparece, y el Commit y el WS bajan |
Las cifras reales varían según el runtime, los productos de seguridad, la presión de memoria y el momento de la observación. No se fije en si llega exactamente a 256 MiB, sino en hacia dónde se mueve entre una etapa y otra.
10.2. Distinguir entre suave y duro en PerfMon
En PerfMon, coloque los siguientes contadores en el mismo eje temporal.
Process(<objetivo>)\\Page Faults/secMemory\\Pages Input/secMemory\\Page Reads/secMemory\\Available MBytesProcess(<objetivo>)\\Working Set - PrivateProcess(<objetivo>)\\Private Bytes
Process\\Page Faults/sec incluye tanto los fallos suaves como los duros. En cambio, Memory\\Pages Input/sec es el número de páginas leídas del disco para resolver fallos duros.10
En la etapa Touch de este programa, aunque Page Faults/sec se dispare, Pages Input/sec no debería aumentar mucho. Como las páginas recién puestas en Commit se materializan mediante demand zero, no hace falta leer los datos originales del disco.
Cabe señalar que, cuando hay varios procesos con el mismo nombre, números como process#1 en PerfMon pueden cambiar al reiniciar. Confírmelo con el contador que muestra el PID, o identifique el proceso mediante el PID con Process V2 o ETW/WPA.
11. Tres malas lecturas que evitar en la práctica
11.1. «El Commit aumentó, así que hay una fuga de RAM»
Commit es la cantidad prometida para conservar el contenido, y una página que no se ha tocado todavía puede no estar residente en RAM. Para determinar si hay una fuga, observe la evolución de Private Bytes en el tiempo, el desglose de las asignaciones y si vuelve al valor de referencia después de procesar.
11.2. «Page Faults/sec es alto, así que el disco va lento»
Los fallos suaves no implican E/S de disco. Observe por separado Page Faults/sec, Pages Input/sec y la espera de almacenamiento, y si es necesario, rastree hasta el archivo de origen y la pila con el evento HardFault de ETW.
11.3. «Vaciar el Working Set arregla la fuga»
Sacar una página del Working Set no libera el Commit ni la propiedad. La página simplemente pasa a Standby o Modified, y más tarde vuelve mediante un fallo. Para corregir una fuga, quien hizo la asignación debe llamar a VirtualFree, liberar el montón o destruir el objeto, entre otras acciones.
Adónde va esa página física una vez retirada lo seguiremos en la 2.ª parte.
12. Resumen
MEM_RESERVEreserva un rango de direcciones virtuales, pero no asigna región física ni en la RAM ni en el archivo de paginación.1MEM_COMMITconsume Commit y garantiza que en el futuro se podrá conservar el contenido, pero la página física normal no se asigna hasta el primer acceso.12- El VAD es el registro de rangos, la PTE el de páginas virtuales y la base de datos PFN el de páginas físicas.
- Un fallo de TLB no es un fallo de página. Si hay una PTE válida, se resuelve solo con el recorrido de la tabla de páginas.
- Demand zero, la recuperación desde Transition y la conexión de páginas compartidas son fallos suaves que se resuelven sin E/S de disco.5
- Si hace falta leer del archivo de paginación, un DLL, un EXE o un archivo mapeado, es un fallo duro.6
- Si al examinar el VAD, la PTE y los atributos de protección no se puede resolver, se produce una excepción como
0xC0000005.7 - Para evaluar el rendimiento, no se fije solo en
Page Faults/sec: observe en el mismo eje temporalPages Input/sec, Available, Working Set, Private Bytes y la espera de almacenamiento.
La continuación es la 2.ª parte, «El ciclo de vida de una página física: las cinco listas y la verdad sobre el archivo de paginación».
Tras convertir la promesa de Commit en una página física, seguiremos, a partir de la base de datos PFN y de las listas de páginas, adónde va esa página cuando sale del Working Set.
Artículos relacionados
- Qué representa el “uso de memoria” de Windows — cómo leer correctamente Working Set, Private Bytes, Commit y el archivo de paginación
- Interioridades de la E/S de Windows (1.ª parte) — la arquitectura de E/S de Windows y la IRP
- Interioridades de la E/S de Windows (4.ª parte) — el administrador de caché y WriteFile
- Análisis de volcados de memoria con WinDbg + SOS
- Introducción a la recolección de volcados de memoria de aplicaciones Windows
Áreas de consultoría relacionadas
KomuraSoft LLC se ocupa de la investigación del uso de memoria de aplicaciones Windows, las infracciones de acceso, la lentitud en el arranque, la paginación y el análisis de defectos en código nativo.
- Desarrollo de aplicaciones Windows
- Investigación de defectos y análisis de causas
- Aprovechamiento de activos existentes y apoyo a la migración
- Contacto
Referencias
-
Microsoft Learn, VirtualAlloc function. Sobre cómo
MEM_RESERVEreserva un rango de direcciones virtuales sin asignar almacenamiento físico, cómoMEM_COMMITcobra una cuota de compromiso contra la memoria y el archivo de paginación de todo el sistema, cómo el contenido inicial de una página en Commit es cero, y cómo la página física real no se asigna hasta que se accede a ella. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn, PERFORMANCE_INFORMATION structure. Sobre cómo
CommitTotales el número actual de páginas en Commit del sistema yCommitLimites el límite máximo de Commit posible sin ampliar el archivo de paginación. ↩ ↩2 -
Microsoft Learn, !vad (WinDbg). Sobre cómo
!vadmuestra el árbol de VAD y permite consultar la VPN inicial y final, el Commit, si es Mapped o Private, los atributos de protección, el Control Area, entre otros datos. ↩ -
Microsoft Learn, PageFault_TypeGroup1 class. Sobre cómo ETW distingue y registra Transition Fault, Demand Zero Fault, Copy-on-Write, Guard Page Fault, Hard Page Fault y Access Violation. ↩
-
Microsoft Learn, Working Set. Sobre cómo un fallo suave se puede resolver sin acceder al backing store, y cómo ocurre por el Working Set de otro proceso, por Transition o por demand zero en la primera referencia, entre otros casos. ↩ ↩2 ↩3
-
Microsoft Learn, PageFault_HardFault class. Sobre cómo el evento HardFault incluye FileObject, ReadOffset, ByteCount, VirtualAddress y Thread ID, lo que permite rastrear el origen de la lectura. ↩ ↩2
-
Microsoft Learn, Access Violation C0000005. Sobre cómo
0xC0000005se produce al leer, escribir o ejecutar una dirección de memoria inválida, y cómo los parámetros de la excepción indican el tipo de acceso y la dirección que infringió la protección. ↩ ↩2 -
Microsoft Learn, Creating Guard Pages. Sobre cómo
PAGE_GUARDproporciona una notificación de un solo uso del acceso a la página y generaSTATUS_GUARD_PAGE_VIOLATION. ↩ -
Microsoft Learn, VMMap - Sysinternals. Sobre cómo VMMap descompone la memoria virtual en Commit por tipo y muestra el Working Set y el mapa de direcciones detallado de cada tipo. ↩
-
Microsoft Learn, Performance Analysis of Logs (PAL) Tool. Sobre cómo
Memory\\Pages Input/seces el número de páginas leídas del disco para resolver fallos de página duros. ↩
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Las entrañas de la memoria de Windows (parte 3) — Objetos de sección y copia en escritura: la verdadera naturaleza de las DLL y el mapeo de archivos
Explica cómo objetos de sección, mapeo de imagen/datos, caché compartida y copia en escritura hacen que las DLL compartan páginas físicas.
Interioridades de la memoria de Windows (parte 2) — El ciclo de vida de la página física: las cinco listas y la verdad sobre el archivo de paginación
Conectamos la base de datos PFN, Standby, Modified, la compresión de memoria y el archivo de paginación, y explicamos adónde va una págin...
¿Qué representa realmente el «uso de memoria» de Windows? — Cómo interpretar correctamente Working Set, Private Bytes, Commit y el archivo de paginación
La memoria de Task Manager, Working Set, Private Bytes y Commit no son el mismo valor. Explica la memoria virtual y física de Windows y q...
Cómo funcionan el portapapeles y arrastrar y soltar — Gestionar correctamente la transferencia de datos OLE en aplicaciones empresariales
Una tabla de Excel se deforma al pegarla y deja de poder pegarse si cierra el origen: es el portapapeles colocando el mismo contenido en ...
WPR/WPA en la práctica — Introducción al análisis de rendimiento del sistema para «todo el PC va lento»
Problemas como «todo el PC va lento» o «el arranque es lento», que el Administrador de tareas no explica, se investigan con WPR/WPA leyen...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Desarrollo de aplicaciones para Windows
Aplicaciones empresariales, integración de dispositivos y herramientas de comunicación, de los requisitos al desarrollo.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Se reserva RAM en el momento en que VirtualAlloc hace MEM_COMMIT?
- En la memoria privada habitual, el Commit consume la capacidad de compromiso del sistema, pero la página física correspondiente no se asigna hasta el primer acceso. La página que se toca por primera vez mediante una escritura obtiene su página física durante el procesamiento de un fallo de demand zero.
- ¿Un fallo de página significa que hay una anomalía o un problema de rendimiento?
- No. Los fallos suaves que no implican E/S de disco, como el demand zero o la recuperación desde Standby, son comportamiento normal. Para evaluar el rendimiento no basta con Page Faults/sec: hay que observarlo junto con Pages Input/sec, la espera de almacenamiento y Available MBytes.
- ¿Un fallo de TLB es lo mismo que un fallo de página?
- Son cosas distintas. Aunque el TLB no tenga la traducción, si la PTE de la tabla de páginas es válida, la CPU simplemente recorre la tabla y vuelve a registrar la traducción. Solo se entra en el fallo de página cuando la PTE es inválida o hay una violación de protección.
- ¿Si el VAD tiene el rango de direcciones, se descarta una infracción de acceso?
- No necesariamente. Además de la existencia del VAD, el administrador de memoria evalúa si el rango está en Reserve o en Commit, los atributos de protección de lectura, escritura y ejecución, las páginas de guarda y el estado de la PTE, entre otros factores. Si no puede resolverse, se produce una excepción como 0xC0000005.
- ¿Un valor alto de Page Faults/sec indica falta de RAM?
- Eso solo no permite concluirlo. Page Faults/sec también incluye un gran volumen de fallos suaves. Es necesario correlacionarlo, en el mismo eje temporal, con Memory\Pages Input/sec, Memory\Page Reads/sec, Available MBytes y el tiempo de espera de disco.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.