Что на самом деле означает «использование памяти» в Windows — как правильно читать Working Set, Private Bytes, Commit и файл подкачки
· Го Комура · Windows, Разработка Windows, Управление памятью, Working Set, Private Bytes, Commit, Файл подкачки, Мониторинг производительности, Диагностика, Sysinternals
Диспетчер задач показывает «Память» процесса как 1,2 ГБ. Но Process Explorer показывает Working Set 1,5 ГБ и Private Bytes 2,4 ГБ, а Size в VMMap ещё больше. Если смотреть систему целиком, читается «Committed 19.6/31.8GB».
Так сколько же гигабайт памяти это приложение в итоге использует?
Ответ в том, что какое число смотреть, зависит от того, что вы на самом деле хотите узнать. Показатель меняется в зависимости от того, хотите ли вы объём, сейчас резидентный в ОЗУ, объём, выделенный именно этому процессу, объём, который система обещала и дальше обеспечивать, или просто диапазон зарезервированных виртуальных адресов.
Путаница с показателями памяти Windows в том, что все они отображаются одним словом «память», хотя на деле измеряют следующие отдельные оси.
- Сколько адресного пространства занято
- Сколько commit потреблено
- Резидентна ли страница сейчас в физической ОЗУ
- Страница частная для процесса или совместно используемая
- Сколько ещё выделения система в целом может поддержать
Статья рассчитана на тех, кто расследует рост памяти приложения или общесистемную нехватку памяти в Windows 10/11 и актуальном Windows Server, и собирает в одну картину связи между Working Set, Private Working Set, Private Bytes, Commit, Virtual Bytes, файлом подкачки, Available и ошибками страниц.
Процедура, как отличить, почему объекты .NET не собираются, подробно разобрана в «.NET: как отличить ожидание GC от утечки памяти», а конкретная работа с VMMap и Process Explorer — в «Process Explorer / Handle / VMMap на практике». Эта статья сосредоточена на общей предпосылке обоих: как читать числа со стороны ОС Windows.
1. Сначала вывод
- Working Set — множество страниц, сейчас резидентных в ОЗУ. В него входят не только страницы, частные для процесса, но и страницы, которые можно разделить с другими процессами, например код DLL и файлы, отображённые в память.1
- Private Working Set — та часть Working Set, которая сейчас принадлежит только этому процессу. Это удобное приближение «ОЗУ, которую этот процесс сейчас занимает один», но не полный объём, который выделило приложение.2
- Private Bytes — объём commit, частный для этого процесса. Это отдельный показатель от того, резидентна ли память сейчас в ОЗУ. Поле
PagefileUsageв структуре Win32 API на актуальной Windows тоже по сути представляет тот же Commit Charge, а не число байт, реально записанных в файл подкачки.2 - «Committed X/Y» в диспетчере задач показывает X как текущий суммарный commit системы и Y как потолок commit. X — не использование файла подкачки. Y примерно определяется ОЗУ плюс файл подкачки.3
- Reserve и Commit — разные вещи. Просто зарезервировать диапазон виртуальных адресов значит отложить его на будущее; это не потребляет тот же объём ни ОЗУ, ни потолка commit.45
- Page Fault не обязательно означает дисковый ввод-вывод. Есть мягкие ошибки, которые разрешаются внутри ОЗУ, и жёсткие, которые читают из файла подкачки, исполняемых файлов, файлов, отображённых в память, и тому подобного.16
- Утечку памяти судят не по одному снятию, а по тенденции при повторении одной и той же нагрузки. Особенно смотрите, продолжают ли Private Bytes и его разбивка ступенчато расти даже после окончания обработки, не возвращаясь к тому же установившемуся состоянию.
Одним предложением: Working Set — «объём, который сейчас в ОЗУ», Private Bytes — «объём, обещанный именно этому процессу», Commit — «объём, который обещала система в целом».
flowchart TB
accTitle: Выбор нужного показателя памяти Windows
accDescr: Какой показатель смотреть, зависит от того, нужна резидентность в ОЗУ, частный commit процесса, общесистемный commit или диапазон виртуальных адресов
question["Что вы хотите узнать об использовании памяти"]
question -->|объём сейчас в ОЗУ| workingSet["Working Set"]
question -->|частный обещанный объём процесса| privateBytes["Private Bytes"]
question -->|общесистемный обещанный объём| systemCommit["System Commit"]
question -->|зарезервированный диапазон адресов| virtualBytes["Virtual Bytes / Reserved"]
workingSet --> resident["Резидентность в физической ОЗУ"]
privateBytes --> privateCommit["Частный Commit процесса"]
systemCommit --> commitLimit["Сравнение с Commit Limit"]
virtualBytes --> addressSpace["Виртуальное адресное пространство"]
Рис. 1: Сначала разложите наблюдение «памяти много» на четыре отдельных вопроса.
2. Разложение «использования памяти» на четыре оси
Для начала думайте о памяти Windows не как о «одном столбике», а по четырём осям.
flowchart TB
accTitle: Четыре независимые оси классификации одной страницы
accDescr: Отдельно проверяйте состояние виртуального адреса, обеспечение закоммиченных страниц, резидентность в физической ОЗУ и возможность разделения с другими процессами
page["Смотрите одну страницу по четырём осям"]
page --> address["Состояние адреса"]
address --> addressValues["Free / Reserved / Committed"]
page --> backing["Обеспечение"]
backing --> backingValues["Page-file-backed / File-backed"]
page --> residentAxis["Резидентность в ОЗУ"]
residentAxis --> residentValues["Resident / Not resident"]
page --> sharing["Совместность"]
sharing --> sharingValues["Private / Shareable"]
Рис. 2: Даже у одной страницы состояние адреса, обеспечение, резидентность и совместность определяются независимо.
Mapped — не состояние адреса рядом с Free, Reserved и Committed, а категория области. Страницы в отображённом представлении тоже могут быть Committed. Так же Private — не носитель обеспечения, а классификация совместности. Поэтому обеспечение читайте как Page-file-backed или File-backed, а совместность — как Private или Shareable, отдельно.
Сочетание этих четырёх осей даёт следующее отношение представительных показателей.
| Состояние страницы | Working Set | Private Working Set | Private Bytes | Семейство Virtual Bytes |
|---|---|---|---|---|
| Частная процессу, закоммичена, резидентна в ОЗУ | Входит | Входит | Входит | Входит |
| Частная процессу, закоммичена, не резидентна в ОЗУ | Не входит | Не входит | Входит | Входит |
| Общая страница DLL или отображённого файла, резидентна в ОЗУ | Входит | Обычно не входит | Обычно не входит | Входит |
| Reserved, но не закоммичена | Не входит | Не входит | Не входит | Может входить |
| Неиспользуемый диапазон адресов | Не входит | Не входит | Не входит | Обычно не входит |
flowchart TB
accTitle: Соответствие типов страниц основным показателям памяти
accDescr: Показывает, какие показатели включают резидентные частные страницы, нерезидентные частные, резидентные общие и только зарезервированные диапазоны
privateResident["Private, закоммичена, в ОЗУ"]
privateNonresident["Private, закоммичена, не в ОЗУ"]
sharedResident["Общая страница, в ОЗУ"]
reservedOnly["Reserved, не закоммичена"]
workingSet["Working Set"]
privateWorkingSet["Private Working Set"]
privateBytes["Private Bytes"]
virtualBytes["Семейство Virtual Bytes"]
privateResident --> workingSet
privateResident --> privateWorkingSet
privateResident --> privateBytes
privateResident --> virtualBytes
privateNonresident --> privateBytes
privateNonresident --> virtualBytes
sharedResident --> workingSet
sharedResident --> virtualBytes
reservedOnly --> virtualBytes
Рис. 3: Working Set и Private Bytes считают разные множества страниц, поэтому между ними нет простого включения.
Важно здесь то, что Working Set и Private Bytes не находятся в простом отношении включения.
Private Bytes включает страницы, частные для процесса, но сейчас не резидентные в ОЗУ. Working Set, напротив, включает общие страницы — код DLL и разделяемую память — которые Private Bytes вообще не считает. Поэтому в зависимости от процесса и момента Working Set может быть больше Private Bytes или наоборот.
Кроме того, простое суммирование Working Set нескольких процессов может посчитать одну и ту же физическую страницу — например общую DLL — несколько раз. «Сумма Working Set каждого процесса равна используемой ОЗУ» не обязательно верно.
3. Виртуальное адресное пространство — Reserve и Commit это разное
3.1. Виртуальный адрес — не адрес физической ОЗУ
У каждого процесса своё частное виртуальное адресное пространство. Указатель, с которым работает приложение, не указывает напрямую на место в физической ОЗУ; Windows через таблицы страниц сопоставляет виртуальные адреса физическим страницам или данным на файле.7
Поэтому даже на ПК с 64 ГБ установленной ОЗУ виртуальное адресное пространство, которое может использовать данный 32-разрядный процесс, обычно намного меньше. И наоборот, нормально, что у 64-разрядного процесса виртуальное адресное пространство больше физической ОЗУ.
3.2. Reserved значит только «адрес застолблён»
MEM_RESERVE у VirtualAlloc резервирует непрерывный диапазон виртуальных адресов на будущее. На этом этапе с страницами не связано физическое хранилище, и диапазон нельзя читать или писать.45
Например, даже если база данных или среда выполнения резервирует диапазон адресов 8 ГБ на будущий рост, одно это не потребляет 8 ГБ ОЗУ и не 8 ГБ Private Bytes.
3.3. Committed — обещание «обеспечить, когда понадобится»
MEM_COMMIT переводит виртуальную страницу в состояние Committed и заставляет Windows обещать нужное обеспечение. Разрешены ли чтение, запись или исполнение, решает отдельно защита страницы — PAGE_READONLY, PAGE_READWRITE, PAGE_EXECUTE, PAGE_NOACCESS и так далее — поэтому само по себе Committed не значит «можно читать и писать». В момент commit это учитывается в системном Commit Charge, но фактическая физическая страница может не назначаться до первого обращения. Страница, к которой обратились впервые, обнуляется, проходит demand-zero fault и входит в Working Set.51
Поэтому, хотя и то и другое называют «выделением», на деле есть следующие три стадии.
flowchart TB
accTitle: Три стадии от Reserve через Commit к резидентности в ОЗУ
accDescr: Показывает поток резервирования виртуального адреса, коммита страницы и назначения физической страницы при первом обращении с входом в Working Set
reserve["MEM_RESERVE - зарезервировать диапазон"]
reserve -.-> virtualMetric["Отражается в семействе Virtual Bytes"]
reserve -->|MEM_COMMIT| committed["Committed - доступна по защите страницы"]
committed -.-> commitMetric["Отражается в Private Bytes / System Commit"]
committed -->|первое обращение, demand-zero fault| resident["Назначена физическая страница, в ОЗУ"]
resident -.-> workingSetMetric["Отражается в Working Set"]
committed -.->|если не обращались| nonresident["Committed, но не резидентна"]
Рис. 4: Reserve, Commit и первое обращение — отдельные события, и каждое двигает свой показатель.
Эти три стадии отдельно двигают числа семейства Virtual Bytes, Private Bytes и Working Set соответственно.
3.4. Почему бывает OutOfMemory при свободной ОЗУ
Успех выделения памяти определяется не только свободной ОЗУ.
- Процесс исчерпал своё виртуальное адресное пространство
- Нет свободного диапазона адресов нужного размера, который был бы непрерывным
- Общесистемный Commit Charge достиг Commit Limit
- У Job Object, контейнера, среды выполнения или библиотеки свой предел
- Это 32-разрядный процесс
- Нативная куча фрагментирована
Даже на 64-разрядной Windows пользовательское виртуальное адресное пространство 32-разрядного процесса обычно 2 ГБ, если не задан IMAGE_FILE_LARGE_ADDRESS_AWARE. 32-разрядное приложение с этим флагом может использовать до 4 ГБ на 64-разрядной Windows.8
Поэтому «у ПК 20 ГБ свободной ОЗУ, а 32-разрядное приложение падает около 1,6 ГБ» — не противоречие. Это может быть вовсе не проблема ОЗУ, а фрагментация адресного пространства или жёсткий предел.
4. Working Set — страницы, которые сейчас в ОЗУ
Working Set — множество страниц в виртуальном адресном пространстве процесса, которые сейчас резидентны в физической ОЗУ.1
В этом множестве смешано следующее.
- Собственные куча и стек процесса
- Код EXE и DLL и данные только для чтения
- Файлы, отображённые в память
- Разделяемая память
- Страницы, ставшие частными для процесса после копирования при записи
- Страницы, к которым обращались среда выполнения и разные библиотеки
4.1. Рост Working Set не обязательно значит, что выделили больше
Первое обращение к уже закоммиченной странице может увеличить только Working Set, тогда как Private Bytes не меняется. Так же, когда большой файл отображают в память и читают последовательно, страницы с файловым обеспечением входят в Working Set, а Private Bytes почти не растёт.
Наоборот, когда Windows делает Trim Working Set в ответ на давление памяти, сжимается только Working Set, хотя приложение логически всё ещё держит ту же память. Повторное обращение возвращает её через Page Fault.
Поэтому падение Working Set не обязательно значит «приложение освободило», а рост — «приложение заново выделило».
flowchart TB
accTitle: Типичный поток, в котором растёт и падает только Working Set
accDescr: Та же закоммиченная страница входит в ОЗУ при первом обращении, становится нерезидентной при Trim и возвращается при повторном обращении, а Private Bytes всё это время учитывается
committed["Та же закоммиченная страница"]
committed -->|первое обращение| resident["Резидентна в ОЗУ"]
resident -->|Trim при давлении памяти| nonresident["Не резидентна"]
nonresident -->|Page Fault при повторном обращении| resident
resident -.-> inWorkingSet["Входит в Working Set"]
nonresident -.-> outsideWorkingSet["Не входит в Working Set"]
committed -.-> privateBytes["Учитывается в Private Bytes, пока закоммичена"]
Рис. 5: Working Set растёт и падает с резидентностью, но Private Bytes не уменьшается, пока commit той же страницы остаётся.
4.2. Working Set включает общие страницы
Если 10 процессов разделяют кодовые страницы одной DLL, эта страница может появиться в Working Set каждого процесса, хотя в физической ОЗУ существует только одна копия. Сумма Working Set, превышающая установленную ОЗУ, сама по себе ещё не признак беды.
Если хотите приблизиться к «ОЗУ, которую этот процесс сейчас занимает один», смотрите Private Working Set. Но и это не «вся память, которую процесс выделил» — строго частные страницы, сейчас резидентные.
4.3. Принудительное сжатие Working Set утечку не чинит
EmptyWorkingSet или SetProcessWorkingSetSize позволяют вытеснить страницы из Working Set процесса. Но это не операция, которая освобождает commit или снимает ссылки в куче. Видимое использование ОЗУ падает, Private Bytes не меняется, а следующее обращение может вызвать всплеск ошибок страниц.9
Если цифра в диспетчере задач уменьшается только сразу после кнопки «уменьшить память» и сразу возвращается, когда вы снова начинаете работать, это может быть просто Trim Working Set, а не настоящее «освобождение».
5. Private Bytes — объём commit, частный для процесса
Private Bytes — объём виртуальной памяти, закоммиченной исключительно для этого процесса. Это Commit Charge, который нельзя разделить с другим процессом, и неважно, резидентна ли она сейчас в ОЗУ. В PROCESS_MEMORY_COUNTERS_EX Microsoft этому значению соответствует PrivateUsage.102
В Win32 API есть ещё поле с запутывающим именем PagefileUsage, но актуальная документация определяет его как «Commit Charge этого процесса» и говорит, что это то же значение, что PrivateUsage. Иными словами, Private Bytes 2 ГБ не значит «в pagefile.sys записано 2 ГБ».2
На Private Bytes обычно влияют следующее.
- Commit нативной кучи, которую используют
HeapAlloc,malloc,newи подобные - Private Data, закоммиченные напрямую через
VirtualAlloc - Закоммиченная область кучи GC .NET
- Та часть стека потока, которая реально закоммичена
- Commit Charge на всё представление, зарезервированное при отображении представления копирования при записи(
FILE_MAP_COPY) - Частные буферы, которые внутри держат библиотеки и SDK устройств
В представлении копирования при записи, созданном через FILE_MAP_COPY, каждая страница в итоге может стать частной, поэтому в момент отображения Windows резервирует Commit Charge, достаточный, чтобы обеспечить всё представление файлом подкачки. Из-за этого System Commit и Commit Charge процесса(Private Bytes)могут вырасти на размер всего представления ещё до того, как любая запись реально создаст частную копию.11
5.1. Почему Private Bytes не падает после free или GC
Даже когда память «освобождена» с точки зрения приложения, среда выполнения или аллокатор кучи может не сделать Decommit этой области обратно ОС, а оставить её для будущего повторного использования. Тогда Private Bytes остаётся высоким, хотя внутри приложения область уже пригодна к повторному использованию.
Он также может оставаться высоким, потому что жива только часть большой области, из-за фрагментации или потому что кэш или пул прогрелись до потолка.
Поэтому высокий Private Bytes сам по себе не доказывает утечку. Смотреть нужно сравнение во времени:
- Повторите одну и ту же обработку одно и то же число раз
- Подождите одно и то же время после обработки
- Проверьте, возвращается ли Private Bytes на тот же уровень или выходит на плато
- Через VMMap или дамп кучи проверьте, какая область или тип выросли
flowchart TB
accTitle: Почему Private Bytes не падает после free или GC
accDescr: Private Bytes меняется по-разному в зависимости от того, возвращает ли аллокатор ОС область, которая приложению больше не нужна, или держит её для повторного использования
release["Приложение освобождает область через free / GC"]
release --> decision{"Возвращает ли аллокатор её ОС"}
decision -->|Decommit / Release| returned["Commit Charge уменьшается"]
returned --> lower["Private Bytes падает"]
decision -->|держит для повторного использования| retained["Область остаётся закоммиченной"]
retained --> high["Private Bytes стоит высоко"]
retained --> reasons["Пулы, кэши, фрагментация"]
Рис. 6: То, что область стала пригодной к повторному использованию внутри приложения, не то же самое, что возврат её Commit ОС.
5.2. Сильный кандидатный рисунок утечки
Рост вроде следующего, где пол «ступеньками» поднимается с каждым раундом нагрузки, заслуживает внимания.
Private Bytes
^
| ________
| ______|
| ______|
|_____|
+----------------------------> Повторения одной и той же обработки
Впрочем, даже лестница может быть несколькими раундами роста от первой JIT-компиляции, шрифтов, декодеров изображений, пулов соединений или прогрева кэша, после чего стабилизируется. Важно не то, что растёт, а то, что не сходится к установившемуся состоянию.
6. System Commit — что на самом деле такое «Committed X/Y»
Цифра «Committed X/Y» на вкладке [Производительность] → [Память] диспетчера задач — общесистемный показатель.
- X: System Commit Charge — закоммиченная память, которую Windows сейчас обещает обеспечить по всей системе
- Y: System Commit Limit — потолок commit, который система может поддержать
Commit Limit примерно определяется физической ОЗУ плюс сумма всех файлов подкачки. Без файла подкачки получается чуть меньше установленной ОЗУ.36
flowchart TB
accTitle: Связь System Commit Charge и Commit Limit
accDescr: Частный, общий секционный и ядерный commit составляют текущее значение X, а физическая ОЗУ и файл подкачки поддерживают потолок Y
processCommit["Private Commit каждого процесса"] --> charge["System Commit Charge - X"]
sharedCommit["Commit общих секций с обеспечением файлом подкачки"] --> charge
kernelCommit["Commit ядра"] --> charge
physicalRam["Физическая ОЗУ"] --> limit["System Commit Limit - Y"]
pageFiles["Файл подкачки"] --> limit
charge -->|X не может превысить Y| limit
Рис. 7: X — текущий обещанный объём, Y — потолок, который может поддержать это обещание; это не отображение использования файла подкачки.
В System Commit Charge входит не только сумма Private Bytes каждого процесса, но и Commit общих секций с обеспечением файлом подкачки и Commit, который потребляет ядро. Поэтому одной суммы попроцессных Private Bytes недостаточно, чтобы полностью объяснить X.
6.1. Commit Charge — не использование файла подкачки
Возьмите систему с 16 ГБ ОЗУ, файлом подкачки 16 ГБ и Committed 20/31 ГБ.
Эти 20 ГБ не значат «в файл подкачки записано 20 ГБ». Это суммарный объём, для которого Windows обещает предоставить обеспечение ОЗУ или файлом подкачки, когда понадобится, для частных записываемых страниц и тому подобного.
В этот момент могут одновременно держаться такие состояния:
- Большая часть резидентна в ОЗУ
- Часть вытеснена в файл подкачки
- Часть закоммичена, но к ней ещё не было первого обращения
- Часть потреблена как commit на стороне ядра
Если хотите увидеть реальное использование файла подкачки, смотрите Paging File(*)\% Usage отдельно от Commit. Даже собственные материалы Microsoft объясняют, что высокое использование файла подкачки само по себе не обязательно указывает на проблему производительности и что судить нужно вместе с достижением Commit Limit, Modified Page List и фактическим вводом-выводом подкачки.6
6.2. Что происходит при приближении к Commit Limit
Когда System Commit Charge достигает Commit Limit, новые запросы commit обеспечить нельзя. Это ведёт к отказам выделения памяти процессов, падениям приложений и неотзывчивости системы.3
Здесь X/Y Commit важнее, чем «свободная ОЗУ». Даже если Trim Working Set освободит ОЗУ, достижение Commit Limit не снимается, пока сам Commit Charge не уменьшится.
6.3. Три роли файла подкачки
Файл подкачки в основном служит следующим ролям.
- Расширение Commit Limit
- Возможность вытеснить из ОЗУ редко используемые изменённые страницы
- Обеспечение системного дампа сбоя, в зависимости от конфигурации
Отключение файла подкачки — не простой случай «дисковый ввод-вывод всегда падает и всё становится быстрее». Скорее оно снижает Commit Limit, делает вероятнее, что изменённые, но сейчас ненужные страницы останутся в ОЗУ, и может сделать невозможным снять нужный дамп при сбое.36
Подходящий размер файла подкачки нельзя решить только по установленной ОЗУ. Microsoft сама объясняет, что обобщать нельзя, потому что пиковый System Commit Charge и нужный вид дампа сбоя различаются от системы к системе.6
7. Разбивка физической ОЗУ — не судите только по низкому Available
Физическая ОЗУ используется не только Working Set пользовательских процессов.
- Working Set каждого процесса
- Системный файловый кэш
- Списки страниц вроде Standby, Modified, Free и Zeroed
- Paged Pool / Nonpaged Pool ядра
- Память, которую держат драйверы устройств
- Хранилище сжатия памяти
- Области, общие с GPU и другими устройствами или зарезервированные для них
- Память, зарезервированная оборудованием
7.1. Available включает и пригодный к повторному использованию кэш
Available MBytes Windows — не просто полностью неиспользуемая ОЗУ. Это показатель, который наряду с Free и Zeroed включает страницы Standby, которые при необходимости можно использовать повторно.12
- Free: страницы, сейчас не выделенные ни под какую цель
- Zeroed: страницы, обнулённые так, что их можно безопасно отдать другому процессу
- Standby: страницы, покинувшие Working Set, но чьё содержимое ещё кэшировано в ОЗУ
- Modified: страницы, чьё содержимое изменилось и которые перед повторным использованием нужно записать в подходящее обеспечение
flowchart TB
accTitle: Движение между Working Set и списками страниц
accDescr: Показывает уход неизменённых страниц в Standby и изменённых в Modified, затем повторное обращение, запись и повторное использование
workingSet["Working Set - в использовании"]
workingSet -->|убирают неизменённую страницу| standby["Standby - кандидат на повтор с сохранением содержимого"]
workingSet -->|убирают изменённую страницу| modified["Modified - ждёт записи"]
modified -->|запись завершена| standby
standby -->|повторное обращение| workingSet
standby -->|повторно под другую цель| reused["Выделена под другую цель"]
free["Free - не используется"] -->|обнуление| zeroed["Zeroed - доступна для нового выделения"]
zeroed -->|обращение после выделения| workingSet
standby -.-> available["Входит в Available"]
free -.-> available
zeroed -.-> available
Рис. 8: Available включает не только полностью свободную память, но и Standby, которую при необходимости можно использовать повторно.
«Выбросить весь кэш, чтобы увеличить свободную ОЗУ» не всегда выигрыш. Если нужные данные ещё сидят в Standby, повторное обращение может быстро вернуть их в Working Set без чтения с диска.
Поэтому даже если Free в диспетчере задач низкий, если Available достаточно и жёсткие ошибки страниц или ожидание диска не создают проблем, Windows может просто эффективно использовать ОЗУ как кэш.
7.2. Когда ОЗУ сжимается без крупного процесса
Нередко потребление памяти остаётся необъяснённым даже после суммирования Private Working Set каждого процесса.
- Файловый кэш и файлы, отображённые в память
- Nonpaged Pool / Paged Pool
- Страницы, заблокированные драйвером
- Общие страницы
- Сжатие памяти
- Выделения, связанные с виртуализацией или GPU
В этом случае, вместо того чтобы продолжать смотреть на список процессов, проверьте Use Counts, Processes, Priority Summary и File Summary в RAMMap из Sysinternals. RAMMap — официальный инструмент, который раскладывает физическую память по назначению, списку страниц и файлу.13
Если растёт только Nonpaged Pool, это момент подозревать утечку на стороне драйвера или ядра, а не Private Bytes приложения в пользовательском режиме.
8. Page Fault — высокий счёт сам по себе не аномалия
Page Fault возникает, когда процесс обращается к странице, которой сейчас нет в его Working Set. Несмотря на слово «Fault» в имени, это не исключительный сбой — это обычный механизм, который двигает виртуальную память.1
8.1. Мягкие ошибки страниц
Разрешаются без чтения с диска.
- Страница ещё в Standby или Transition
- Та же общая страница уже есть в Working Set другого процесса
- К закоммиченной странице обратились впервые и назначили нулевую страницу
- Упреждающее чтение диспетчера памяти уже внесло её в ОЗУ
Поэтому большой \Memory\Page Faults/sec не обязательно значит, что идёт дисковый ввод-вывод или задержка.
8.2. Жёсткие ошибки страниц
Требуют чтения содержимого из хранилища обеспечения на диске. Источник не ограничен файлом подкачки.
- Код и данные в
.exeили.dll - Файл, отображённый в память
- Файл подкачки
flowchart TB
accTitle: Ветвление мягких и жёстких ошибок страниц
accDescr: При обращении к странице вне Working Set это мягкая ошибка, если ввод-вывод хранилища не нужен, и жёсткая, если нужен
access["Обращение к странице вне Working Set"] --> storageIo{"Нужен ли ввод-вывод хранилища"}
storageIo -->|Нет - Standby, общая, demand-zero и т.д.| soft["Мягкая ошибка страницы"]
soft --> resident["Входит в Working Set без чтения диска"]
storageIo -->|Да| hard["Жёсткая ошибка страницы"]
hard --> source{"Откуда читается"}
source --> image["EXE / DLL"]
source --> mapped["Файл, отображённый в память"]
source --> pagefile["Файл подкачки"]
image --> loaded["Входит в Working Set после загрузки"]
mapped --> loaded
pagefile --> loaded
Рис. 9: Одно имя «Page Fault» не скажет, был ли дисковый ввод-вывод.
Microsoft среди счётчиков для измерения жёстких ошибок перечисляет \Memory\Pages/sec, \Memory\Page Reads/sec и \Memory\Pages Input/sec. Поскольку их высота не обязательно значит нехватку памяти, соотносите их с Available MBytes, задержкой диска и фактическим временем отклика.6
8.3. Не ставьте один общий порог
Фиксированное значение вроде «всё выше 1000 Page Faults/sec аномально» меняет смысл в зависимости от хранилища, размера страницы, нагрузки и локальности доступа.
На практике выстройте следующее на одной временной оси.
Memory\Available MBytesMemory\Pages Input/secMemory\Page Reads/sec- Задержка чтения / Queue целевого диска
- Working Set и Private Bytes целевого процесса
- Время обработки приложения, таймауты и отзывчивость интерфейса
Если Available падает одновременно с ростом нагрузки, Pages Input/sec и ожидание диска растут, и время обработки тоже ухудшается, это даёт основания подозревать подкачку из-за давления физической памяти.
9. Какой экран или инструмент смотреть для чего
| Что хотите узнать | Показатель, который смотреть первым | Основные инструменты |
|---|---|---|
| Объём, который целевой процесс сейчас держит в ОЗУ | Working Set | Диспетчер задач, Process Explorer, Get-Process |
| Частную часть этого — ОЗУ, частную процессу | Private Working Set / Working Set - Private | Столбцы «Подробности» диспетчера задач, Process Explorer, PerfMon |
| Объём commit, частный целевому процессу | Private Bytes / Commit Size | Process Explorer, PerfMon, VMMap, Get-Process |
| Диапазон виртуальных адресов процесса | Virtual Bytes / Size | Process Explorer, VMMap, Get-Process |
| Общесистемный запас commit | Committed Bytes / Commit Limit | Диспетчер задач [Производительность], PerfMon |
| Запас повторного использования физической ОЗУ | Available MBytes | Диспетчер задач, PerfMon |
| Разбивку Standby, Modified и файлового кэша | Список страниц / разбивка по назначению | RAMMap |
| Что выросло внутри Private Bytes | Heap / Private Data / Managed Heap и т. д. | VMMap, WinDbg, дампы конкретной среды выполнения |
| Подкачку с участием диска | Pages Input/sec, Page Reads/sec, задержка диска | PerfMon, WPR/WPA |
flowchart TB
accTitle: Выбор инструмента расследования памяти Windows
accDescr: Инструмент зависит от того, цель — один процесс или вся система, одна точка во времени или ряд, и нужно ли проследить удержание внутри среды выполнения
question["Что хотите изолировать"]
question --> processScope{"Цель — один процесс?"}
processScope -->|да| processTime{"Одна точка или временной ряд"}
processTime -->|разбивка одной точки| vmmap["VMMap"]
processTime -->|временной ряд| perfmon["PerfMon / PowerShell"]
processScope -->|вся система| systemView{"Разбивка физической ОЗУ или шкала времени"}
systemView -->|разбивка физической ОЗУ| rammap["RAMMap"]
systemView -->|шкала с ЦП, вводом-выводом и ожиданиями| wpa["WPR / WPA"]
question --> runtime{"Нужно ли проследить удержание внутри среды"}
runtime -->|куча .NET| dotnet["dotnet-dump / PerfView"]
runtime -->|нативная куча| native["WinDbg / Application Verifier"]
Рис. 10: Сначала решив область и шкалу времени, вы выбираете ровно тот инструмент, который нужен, не больше и не меньше.
9.1. Диспетчер задач
В диспетчере задач смотрите экраны раздельно.
- [Процессы] или [Подробности]: семейства Working Set и Commit Size отдельных процессов
- [Производительность] → [Память]: общесистемные In use, Available, Committed, Cached, Paged pool, Non-paged pool
Не судите только по столбцу с именем «Память» — щёлкните правой кнопкой заголовки столбцов на вкладке [Подробности] и добавьте нужные столбцы, например Working Set, Peak Working Set и Commit Size. Имена столбцов немного различаются в зависимости от версии Windows и языка отображения, поэтому подтвердите, что столбец на самом деле означает, прежде чем записывать.
9.2. Снятие временного ряда через PowerShell
Если известен идентификатор целевого процесса, тенденции Working Set, Private Bytes и Virtual Bytes можно снять вместе через Get-Process.
param(
[Parameter(Mandatory)]
[int]$ProcessId,
[int]$IntervalSeconds = 5,
[int]$SampleCount = 60
)
$samples = for ($i = 0; $i -lt $SampleCount; $i++) {
$process = Get-Process -Id $ProcessId -ErrorAction Stop
[pscustomobject]@{
Timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
ProcessId = $process.Id
WorkingSetMB = [math]::Round($process.WorkingSet64 / 1MB, 1)
PrivateBytesMB = [math]::Round($process.PrivateMemorySize64 / 1MB, 1)
VirtualBytesMB = [math]::Round($process.VirtualMemorySize64 / 1MB, 1)
Handles = $process.HandleCount
Threads = $process.Threads.Count
}
Start-Sleep -Seconds $IntervalSeconds
}
$samples | Format-Table -AutoSize
$samples | Export-Csv .\memory-samples.csv -NoTypeInformation -Encoding utf8
Process.WorkingSet64 в .NET соответствует Working Set, PrivateMemorySize64 — Private Bytes, VirtualMemorySize64 — Virtual Bytes.141516
Для приложения с несколькими экземплярами отслеживайте по PID, а не по имени. Для длительного мониторинга, где перезапуск меняет PID, проектируйте сбор так, чтобы записывать время старта, имя службы и подобное, чтобы цель никогда не путалась.
9.3. Система и процесс на одной шкале времени через PerfMon
Запись как минимум следующего вместе сильно упрощает изоляцию.
\Process(<цель>)\ID Process
\Process(<цель>)\Working Set
\Process(<цель>)\Working Set - Private
\Process(<цель>)\Private Bytes
\Process(<цель>)\Virtual Bytes
\Memory\Available MBytes
\Memory\Committed Bytes
\Memory\Commit Limit
\Memory\Pages Input/sec
\Memory\Page Reads/sec
\Memory\Pool Nonpaged Bytes
\Memory\Pool Paged Bytes
Когда несколько процессов разделяют одно имя или во время мониторинга происходит перезапуск, одного имени экземпляра — Process(name) или Process(name#N) — недостаточно, чтобы зафиксировать цель. Записывайте ID Process для каждой выборки и принимайте только тот экземпляр, чьё значение совпадает с отслеживаемым PID. Когда охватываете перезапуск, меняющий PID, отдельно записывайте и время переключения.
Имена счётчиков производительности Windows могут локализоваться в зависимости от языка отображения. Если прямое указание английского имени в PowerShell не находит счётчик, добавьте его через графический интерфейс PerfMon или проверьте имена в локальной среде через Get-Counter -ListSet *.
9.4. Не путайте роли VMMap и RAMMap
- VMMap: раскладывает виртуальную память и Working Set одного процесса на Heap, Image, Mapped File, Private Data, Managed Heap и подобное
- RAMMap: раскладывает физическую ОЗУ всей системы по назначению, списку страниц, процессу и файлу
«Из-за чего выросли Private Bytes этого процесса» — работа VMMap; «на что ушла ОЗУ, которую не объясняет список процессов» — работа RAMMap.1713
10. Чтение симптомов по сочетаниям чисел
| Наблюдаемый рисунок | Первая гипотеза | Что проверять дальше |
|---|---|---|
| Working Set растёт, Private Bytes стабилен | Первое обращение к существующим страницам, общая DLL, отображённый файл, файловый кэш | Image / Mapped File в VMMap, Pages Input/sec |
| Private Bytes растёт, Working Set стабилен | Частный Commit вырос, но нерезидентен или Trim-нут | Heap / Private Data / Managed Heap в VMMap |
| Оба растут сразу после старта, затем выравниваются | JIT, кэш, пул, прогрев инициализации | Растёт ли снова при той же дополнительной нагрузке |
| Пол Private Bytes поднимается с каждым раундом нагрузки | Утечка, кэш без потолка или аллокатор, который держит память после освобождения | Снимки VMMap до и после, дамп кучи |
| Только Working Set внезапно падает и возвращается с активностью | ОС или приложение сделали Trim Working Set | Private Bytes, Pages Input/sec, время отклика |
| X в Committed X/Y приближается к Y | Общесистемное давление commit | Крупнейшие потребители Private Bytes, Paged/Nonpaged Pool, настройки файла подкачки |
| Available низок, Pages Input/sec и задержка диска высоки | Давление физической ОЗУ и жёсткая подкачка | Крупнейшие потребители Working Set, RAMMap, корреляция нагрузки |
| Использование ОЗУ высоко, но крупного процесса нет | Кэш, общие страницы, пулы ядра, драйверы, сжатие и т. д. | RAMMap, Pool Nonpaged/Paged Bytes |
| Свободная ОЗУ есть, а падает только 32-разрядное приложение | Потолок виртуального адресного пространства или фрагментация | Free/Reserved в VMMap, настройка LAA исполняемого файла |
| Private Bytes высок, но не растёт при повторной обработке | Пул или кэш, возможно держащий высокую водяную отметку | Его предел, поведение повторного использования, стабильность после пика |
Самое важное в этой таблице — читать её в сочетании, а не по одному значению.
11. Практическая процедура расследования утечки памяти
11.1. Сначала зафиксируйте условия воспроизведения и точку установившегося режима
Одного «растёт за несколько дней» недостаточно для сравнения.
- Какую долю прогрева после старта включать
- Из чего состоит один цикл работы
- Сколько секунд ждать после одного цикла
- Сколько раундов нужно, чтобы достичь потолка кэша
- Можно ли использовать один и тот же ввод для исправной и проблемной сборок
Решите всё это.
11.2. Записывайте процесс и систему одновременно
Как минимум ведите следующий журнал с одними и теми же метками времени.
- Working Set цели
- Private Bytes цели
- Virtual Bytes цели
- Committed Bytes / Commit Limit системы
- Available MBytes
- Pages Input/sec
- Число хендлов, число потоков
- Число операций или обработанных элементов
Если Private Bytes процесса стабилен, а системный Commit продолжает расти, нужно расширить обзор на другие процессы, ядро, драйверы и общие секции.
11.3. Сначала решите, какое «измерение» растёт
- Только Working Set: резидентные страницы, общие или файловые, Trim и повторная загрузка
- Private Bytes: частный commit процесса
- Только Virtual Bytes: Reserve, отображение, фрагментация адресного пространства
- Только System Commit: включая другие процессы и сторону ядра
- Nonpaged Pool: сторона драйвера/ядра
- Handles / GDI / USER: утечки ресурсов помимо памяти
Пропустить этот порядок и сразу снимать дамп значит читать гору информации, целясь не туда.
11.4. Переходите к разбивке
- Нативный процесс: VMMap, WinDbg, Application Verifier, трассировка кучи
- .NET:
dotnet-counters,dotnet-gcdump,dotnet-dump, PerfView - Вся система: RAMMap, PerfMon, WPR/WPA
- Пул ядра: PoolMon, WinDbg
VMMap показывает закоммиченную виртуальную память процесса и Working Set, выделенный каждой её части, в разбивке по типу. Насколько удаётся сузить рост Private Bytes до Heap, Private Data, Managed Heap или Mapped File, сильно меняет стоимость дальнейшего расследования.17
11.5. После исправления сравнивайте тенденцию в тех же условиях
Недостаточно, чтобы пиковое значение отличалось до и после исправления. Если отличается стартовое значение, сравнение легко переворачивается.
- Одно и то же состояние старта
- Один и тот же ввод
- Одно и то же число операций
- Одно и то же время ожидания
- Один и тот же интервал выборки
— и сравнивайте значение пола и тенденцию после каждого цикла. Доказательство исправления утечки — не «максимум стал меньше», а то, что рост теперь сходится, даже когда та же нагрузка повторяется.
12. Переформулировка распространённых заблуждений
Заблуждение 1: Память в диспетчере задач равна полному объёму, который выделило приложение
Переформулировка: Проверьте, какой это столбец. Для семейства Working Set это объём, сейчас резидентный в ОЗУ; для семейства Commit Size — commit, частный этому процессу.
Заблуждение 2: Private Bytes равны байтам в файле подкачки
Переформулировка: Private Bytes — частный Commit Charge. Это логический обещанный объём, который включает и страницы, сейчас в ОЗУ, и страницы, которые при необходимости обеспечит файл подкачки.
Заблуждение 3: Commit X/Y равен использованию / ёмкости файла подкачки
Переформулировка: X — общесистемный Commit Charge, Y — Commit Limit. Файл подкачки расширяет Y, но X не переводится напрямую в использование на диске.
Заблуждение 4: Высокий Page Faults/sec значит свопинг на диск
Переформулировка: Сюда входят и мягкие ошибки. Проверьте Pages Input/sec, Page Reads/sec и задержку диска, чтобы увидеть, участвует ли реально дисковый ввод-вывод.
Заблуждение 5: Низкая свободная ОЗУ значит нехватку памяти
Переформулировка: Смотрите Available, Standby, жёсткую подкачку и время отклика. Заполнять ОЗУ пригодным к повторному использованию кэшем нормально.
Заблуждение 6: Сжать Working Set значит починить утечку памяти
Переформулировка: Возможно, вы только вытеснили страницы из ОЗУ. Проверьте, уменьшились ли реально Private Bytes и то, что удерживается внутри кучи.
Заблуждение 7: Рост Private Bytes подтверждает утечку
Переформулировка: Судить можно только после проверки, сходится ли это при повторении одной и той же нагрузки, какой вид памяти вырос и не является ли это освобождаемым кэшем.
13. Итоги
- «Использование памяти» Windows — не одно число. Думайте отдельно об адресном пространстве, commit, резидентности в ОЗУ и совместности.
- Working Set — страницы, которые сейчас в ОЗУ, включая и Private, и Shared. Private Working Set — частные резидентные страницы процесса внутри этого.
- Private Bytes — частный Commit Charge процесса; это ни объём, сейчас находящийся в ОЗУ, ни объём, реально записанный в файл подкачки.
- Committed X/Y — общесистемные Commit Charge / Commit Limit. Файл подкачки в основном поддерживает Commit Limit, вытеснение изменённых страниц и дампы сбоя.
- Зарезервированный виртуальный адрес, закоммиченная страница и страница, к которой реально обратились и которая вошла в Working Set, — отдельные стадии.
- Page Fault — нормальная работа, и мягкая ошибка не читает с диска. Жёсткие ошибки тоже могут идти не только из файла подкачки, но из EXE, DLL или отображённого файла.
- Утечка памяти доказывается не размером в одной точке времени, а значением пола и тенденцией после той же нагрузки вместе с разбивкой.
- Базовый путь: VMMap для разбивки отдельного процесса, RAMMap для общесистемной физической ОЗУ, PerfMon для временного ряда и специализированный инструмент дампа для того, что происходит внутри среды выполнения.
В следующий раз, заметив в диспетчере задач, что «память растёт», начните с этого вопроса.
Растёт Working Set, Private Bytes, Virtual Bytes или System Commit?
Один этот вопрос делает точку входа в расследование заметно точнее.
Похожие статьи
- .NET: как отличить ожидание GC от утечки памяти — практические шаги наблюдения, сравнения и доказательства роста памяти
- Process Explorer / Handle / VMMap на практике — ловим зависания, утечки и «файл используется» по состоянию прямо сейчас
- Разделяемая память: подводные камни и практические рекомендации
- Глубины ввода-вывода Windows (часть 4) — Cache Manager: когда ваш WriteFile на самом деле достигает диска?
- Расследование краша промышленной камеры при длительной работе — часть про утечку хендлов
Смежные области консультирования
KomuraSoft LLC проводит расследования первопричин, которые сочетают PerfMon, VMMap, RAMMap, WinDbg и диагностические инструменты .NET, для роста памяти Windows-приложений, деградации производительности после долгой работы, OutOfMemory в 32-разрядных процессах и нехватки памяти, которая возникает только в среде заказчика. Мы не останавливаемся на просто «памяти много» — изолируем, какая область выросла, через какую операцию, почему и откуда на неё ссылаются или её удерживают.
- Разработка приложений для Windows
- Расследование ошибок и причин
- Технические консультации и ревью дизайна
- Связаться с нами
Справочные ссылки
-
Microsoft Learn, Working Set. О том, что Working Set процесса — множество страниц, сейчас резидентных в физической памяти, включая общие страницы; о разнице между мягкими и жёсткими ошибками страниц; о страницах Transition; и об удалении страниц из Working Set. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PROCESS_MEMORY_COUNTERS_EX2 structure. Об определениях WorkingSetSize, PrivateWorkingSetSize, PrivateUsage и SharedCommitUsage и о том, что и PagefileUsage, и PrivateUsage представляют Commit Charge процесса. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Introduction to page files. О том, что файл подкачки поддерживает вытеснение изменённых страниц, системные дампы сбоя и расширение System Commit Limit; об определениях System Commit Charge и Commit Limit; и об измерении через диспетчер задач и счётчики производительности. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Page State. О состояниях Free, Reserved и Committed виртуальной страницы и о том, что со страницами Reserved не связано физическое хранилище и они недоступны. ↩ ↩2
-
Microsoft Learn, VirtualAlloc function. О разнице между MEM_RESERVE и MEM_COMMIT; о том, что commit учитывается против общей памяти системы и файла подкачки; и о том, что фактическая физическая страница иногда не выделяется до первого обращения. ↩ ↩2 ↩3
-
Microsoft Learn, How to determine the appropriate page file size for 64-bit versions of Windows. О том, что размер файла подкачки зависит от пикового Commit Charge и требований к дампу сбоя; о том, что жёсткие ошибки страниц читают не только из файла подкачки, но и из EXE, DLL и файлов, отображённых в память; и о связанных счётчиках производительности. ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Microsoft Learn, Virtual Address Space. О том, что у каждого процесса своё независимое виртуальное адресное пространство и таблица страниц, и виртуальный адрес сам по себе не является физическим адресом. ↩
-
Microsoft Learn, Memory Limits for Windows and Windows Server Releases. О том, что пользовательское виртуальное адресное пространство 32-разрядного процесса обычно 2 ГБ и на 64-разрядной Windows становится 2 ГБ или 4 ГБ в зависимости от IMAGE_FILE_LARGE_ADDRESS_AWARE. ↩
-
Microsoft Learn, SetProcessWorkingSetSize function. О том, что минимум и максимум Working Set не гарантируют резидентность; о возможности опустошить Working Set; и о том, что чрезмерные настройки или операции могут ухудшить производительность системы. ↩
-
Microsoft Learn, Memory Performance Information. О соответствии между счётчиками производительности Windows, API управления памятью и отображением диспетчера задач, включая Working Set / Working Set - Private / Private Bytes объекта Process и Committed Bytes / Commit Limit объекта System. ↩
-
Microsoft Learn, MapViewOfFile function. О том, что
FILE_MAP_COPYделает каждую страницу потенциально копируемой при записи, поэтому Commit Charge на всё представление резервируется для обеспечения файлом подкачки в момент отображения. ↩ -
Microsoft Learn, Understanding Node Metrics and Properties in HPC Cluster Manager. О том, что Available Physical Memory считается как сумма списков Zeroed, Free и Standby, и о значении каждого из этих списков страниц. ↩
-
Microsoft Sysinternals, RAMMap. Об анализе использования физической памяти Windows по назначению, списку страниц, процессу, приоритету, физической странице и файлу. ↩ ↩2
-
Microsoft Learn, Process.WorkingSet64 Property. О том, что
WorkingSet64возвращает Working Set процесса в байтах, соответствуя счётчику производительности Working Set объекта Process. ↩ -
Microsoft Learn, Process.PrivateMemorySize64 Property. О том, что
PrivateMemorySize64возвращает память, частную процессу и не разделяемую с другими процессами, соответствуя счётчику производительности Private Bytes. ↩ -
Microsoft Learn, Process.VirtualMemorySize64 Property. О том, что
VirtualMemorySize64возвращает объём виртуальной памяти, выделенной процессу, соответствуя счётчику производительности Virtual Bytes. ↩ -
Microsoft Sysinternals, VMMap. О разбивке закоммиченной виртуальной памяти процесса по типу и отображении физической памяти(Working Set), выделенной каждой части, вместе с подробной картой памяти. ↩ ↩2
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Глубины памяти Windows (часть 2) — жизнь физической страницы: пять списков и правда о файле подкачки
Статья связывает базу PFN, Standby, Modified, сжатие памяти и файл подкачки и объясняет, куда уходит физическая страница после выхода из ...
Глубины памяти Windows (часть 1) — Миг, когда виртуальный адрес становится физической RAM: page fault от начала до конца
Статья связывает VirtualAlloc, VAD, таблицы страниц, TLB, demand-zero и жёсткие ошибки и объясняет миг, когда виртуальному адресу назнача...
Как читать коды ошибок Windows — трёхслойная структура Win32, HRESULT и NTSTATUS
Когда появляется 0x80004005, разберите его до поиска. Статья о трёх слоях Win32, HRESULT и NTSTATUS, главном шаблоне 0x8007xxxx и поиске ...
OneDrive «Файлы по запросу» и бизнес-приложения — какие допущения ломают заполнители и как с этим жить
CSV с рабочего стола не открывается, или импорт падает с «файл не найден» — причина может быть в Known Folder Move и «Файлах по запросу» ...
Глубины памяти Windows (часть 3) — объекты секций и копирование при записи: чем на самом деле являются DLL и проекции файлов
Статья связывает объекты секций, проекции образа и данных, общий кэш и копирование при записи, чтобы объяснить, как DLL и общая память де...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Разработка приложений для Windows
Бизнес-приложения, интеграция оборудования и средства связи — от требований до разработки.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Показывает ли столбец «Память» в диспетчере задач всю память, которую выделило приложение?
- Нет. В диспетчере задач несколько столбцов памяти — семейство Working Set, семейство Private Working Set, Commit Size и другие — и смысл зависит от того, какой экран и столбец вы смотрите. Working Set — страницы, которые сейчас резидентны в ОЗУ; Private Bytes или Commit Size — собственный объём commit этого процесса. Не читайте один столбец «Память» как полную ёмкость, которую выделило приложение, или как размер утечки.
- В чём разница между Working Set и Private Bytes?
- Working Set — объём страниц, видимых этому процессу и сейчас резидентных в физической ОЗУ, включая совместно используемые страницы вроде кода DLL и файлов, отображённых в память. Private Bytes — объём закоммиченной памяти, которую использует исключительно этот процесс, независимо от того, резидентна ли она сейчас в ОЗУ. Поэтому они никогда не равны, и ни один из них не бывает стабильно больше другого.
- Означает ли «Committed 18/32GB» в диспетчере задач, что 18 ГБ записаны в файл подкачки?
- Нет. Левая цифра — суммарный commit, который система в целом сейчас обещает обеспечить; правая — потолок commit, который система может поддержать. Потолок примерно определяется ОЗУ плюс файл подкачки, но не весь левый итог реально лежит в файле подкачки. Большинство закоммиченных страниц в ОЗУ, а некоторым закоммиченным страницам физическая страница ещё ни разу не назначалась. Страницы же, которые можно заново загрузить из исходного файла — EXE, DLL и отображённые в память файлы — не обязательно поднимают частный Commit на ту же величину, что Working Set.
- Можно ли получить OutOfMemory, когда свободная ОЗУ ещё есть?
- Да. Выделение может провалиться по причинам, не связанным с физической ОЗУ: исчерпание виртуального адресного пространства 32-разрядного процесса, нехватка непрерывного свободного диапазона адресов, системный потолок commit, либо пределы Job Object или среды выполнения. В частности, 32-разрядный процесс на 64-разрядной Windows обычно ограничен 2 ГБ пользовательского виртуального адресного пространства, если он не Large Address Aware.
- Станет ли Windows быстрее, если отключить файл подкачки?
- В общем случае этого нельзя предполагать. Отключение файла подкачки снижает системный потолок commit, затрудняет вытеснение неиспользуемых изменённых страниц из ОЗУ и влияет на настройку дампов при сбое. Размер файла подкачки нужно решать, измерив пиковый commit charge и нужный дамп сбоя — это не параметр, который отключают без обоснования.
- Означает ли высокий Page Faults/sec нехватку памяти в системе?
- По одному этому показателю не скажешь. Ошибки страниц включают мягкие, которые разрешаются страницами Standby в ОЗУ или страницами, общими с другим процессом, и жёсткие, которые читают с диска. Вместо изолированного Page Faults/sec смотрите Pages Input/sec, Page Reads/sec, Available MBytes, задержку диска и время обработки вместе на одной временной оси.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.