Продолжается подписка на наши издания! Вы не забыли подписаться?

Принципы управления памятью в Windows

Автор: Slava Oks
Опубликовано: 18.04.2007

Обсуждая работу серверов приложений и сборщиков мусора (GC) виртуальных машин, многие делают некорректные допущения относительно выделения памяти в ОС. Этому вопросу посвящены монументальные труды именитых авторов, например, Рихтера или Руссиновича. Однако не все прикладные программисты могут позволить себе чтение объемистых трудов по низкоуровневым аспектам работы ОС. Поэтому мы решили опубликовать перевод блогов Slava Oks, одного из разработчиков Microsoft SQL Server, так как они в сжатой форме описывают общую идеологию управления памятью в Windows. Надеемся, эта информация окажется полезной для вас, а может, и подтолкнет к чтению упомянутых монументальных трудов. :)

Взгляд на Виртуальное адресное пространство (Virtual Address Space, VAS)

Память – это набор общих ресурсов, совместно используемых приложениями, их компонентами и операционной системой. Нужно быть очень точным, говоря о каком-либо ресурсе памяти. По ошибке, зачастую, разработчики, пользователи, АБД, ссылаются на разные виды ресурсов, связанных с памятью, используя одно обобщенное понятие «память». Это прекрасно работало во времена DOS, когда все было просто, но тогда не было виртуальной памяти, и приложению не приходилось разделять один компьютер с другими. С тех пор многое изменилось.

Есть несколько типов ресурсов, связанных с памятью. Ресурсами может управлять ОС или пользовательский код, так что каждый разработчик должен полностью понимать, что это за ресурсы.

Как я и обещал, сегодня я расскажу о Virtual Address Space, VAS. В предыдущем сообщении, http://weblogs.asp.net/slavao/archive/2005/01/26/360759.aspx, я показал, как можно будет контролировать VAS в Yukon (классно, правда?). VAS – это связанный с памятью ресурс, который часто игнорируют при проблемах с памятью. Им часто пренебрегают в книгах, описывающих менеджер памяти. Даже в Windows Task Manager нет счетчика VAS для процесса (лентяи!). Как выясняется, понимание работы Windows с VAS процесса весьма важно в случаях проблем с памятью.

У каждого процесса есть собственный VAS, просто, не так ли? Windows создает отдельный VAS для нового процесса при вызове функции CreateProcess. Это позволяет разработчикам занимать/освобождать регионы VAS, используя API VirtualAlloc*/VirtualFree*. Регион VAS может быть привязан к физической памяти сразу же или позже, по мере надобности. Если вы решите зафиксировать регион VAS сразу же, ОС сперва разместит регион VAS, и только затем зафиксирует его. Минимальный размер региона VAS – 64kb (это очень важно!). Размеры всех регионов VAS кратны 64kb. В свою очередь, привязка VAS к физической памяти выполняется на основе размера страницы памяти, 4kb или 8kb (бывают также большие страницы на x86 и IA64, размером 4MB и 16MB, но о них мы поговорим когда-нибудь потом). Это очень важный момент. Любое выделение с использованием VirtualAlloc* округляется до границы в 64kb, так что если вы размещаете новый регион VAS, привязанный к физической памяти , ОС займет объем физической памяти, округленный до размера страницы, и использует VAS процесса, округленный до границы в 64kb (когда я впервые узнал это, это меня шокировало!). Допустим, вы вызываете VirtualAlloc для 1024 байт c флагом COMMIT. ОС займет регион VAS в 64kb в указанном процессе и выделит страницу, 4kb или 8kb, физической памяти. Итог в том, что нужно быть очень внимательным при использовании функций VirtualAlloc*. Если вы их не используете, все равно нужно понимать все тонкости, например, для целей отладки. Все чаще и чаще на платформе x86 можно увидеть процесс, которому не хватает VAS. Чтобы уметь отслеживать такие случаи, нужно знать, что такое VAS и как оно используется, и понимать, что действительно можно столкнуться с нехваткой VAS на 64-битной системе :-). (В нашей тестовой лаборатории при работе с «большими» машинами мы несколько раз выходили за пределы VAS и, кстати, сейчас мне передали баг как раз с этой проблемой, но на «меньшей» машине, что совсем не смешно. При разработке приложения нужно убедиться, что вы не масштабируете структуры данных соответственно объему физической RAM, установленной на машине).

Как уже говорилось, у каждого процесса – свой VAS. Всё, что занимается, загружается, создается и отображается в вашем процессе, использует VAS так, чтобы все эти механизмы использовали вызов VirtualAlloc* до того, как они попробуют что-либо сделать. Например, загрузчик до загрузки DLL в VAS процесса вызовет аналог VirtualAlloc* чтобы разместить регион VAS для DLL; при создании потока в процессе ОС сперва зарезервирует регион VAS для стека потока. То же случится при отображении файла на пространство процесса.

Чтобы компонент мог использовать или занять регион VAS, он должен использовать какой-то тип вызова VirtualAlloc*. ОС оперирует объектом-дескриптором виртуальных адресов – VAD, структурой, описывающей регион VAS. Объект VAD полностью описывает регион VAS, т.е. его размер, атрибуты размещения и защиту. Как можно ожидать, ОС поддерживает дерево VAD. Дерево VAD позволяет ОС эффективно управлять VAS. Посмотреть на дерево VAD вашего процесса можно с помощью локального отладчика ядра windbg (поверьте, это круто!). Более того, как вы думаете, что делает API VirtualQuery? Да, вы правы, он обходит дерево VAD процесса, и, да, это именно то, что мы используем в SQL Server, чтобы генерировать представление sys.dm_os_virtual_address_dump. Как вы можете видеть, Windows приходится управлять VAS процесса так же, как вы управляли бы любым типом кучи с наименьшим размером блока в 64kb (можете попрактиковаться и написать собственный вариант, но, к сожалению, нет способа его подключить :-)).

Как уже говорилось, VAS – очень важный ресурс, который используется множеством разных компонентов, загруженных в пространство процесса, так что возможность его вычислять или отслеживать очень важна. Составить представление о том, какая часть VAS используется процессом, можно с помощью счетчика виртуальных байтов perfmon. В Task manager такого счетчика нет.

К сожалению, Windows не предоставляет простого способа отслеживания и подсчета занятого пространства VAS. То, что ОС не предоставляет такого механизма, не значит, такой механизм нельзя создать. Есть несколько инструментов, которые можно использовать для отслеживания VAS. Один из них – vadump.exe. Я думаю, что VADUMP обходит дерево VAD процесса, используя эквивалент VirtualQuery API. Он предоставляет информацию о регионах VAS, пригодную для последующего анализа.

VADUMP дает массу информации, но во многих случаях этого недостаточно, чтобы разобраться с проблемами в VAS, то есть утечками. Для многих регионов VAS VADUMP не дает разработчику информации о том, какому компоненту принадлежит регион. Один из способов получить такую информацию – подключиться к вызову VirtualAlloc/VirtualFree  (думаете, этого хватит? :)), а затем отслеживать все VAS-операции. Это как раз то, чем занимается инструмент LeakDiag. Он использует библиотеку detours, разработанную Microsoft Research для подключения к VAS API. Для каждого размещения он запоминает информацию о размещении и стек. В любой момент времени информацию можно сбросить в файл и проанализировать. Насколько я знаю, LeakDiag – это сейчас единственное средство, способное выполнять отслеживание VAS на таком уровне (я могу ошибаться, но всегда радует мысль, что ваш инструмент – единственно возможный :-)). Взять LeakDiag можно с ftp://ftp.microsoft.com/PSS/Tools/Developer%20Support%20Tools/LeakDiag/

 Вот основные положения, которые я хотел отразить:

Надеюсь, что эти положения осмысленны, и что вы примете их близко к сердцу. :) Понимание того, как работает VAS, – это первый шаг в понимании менеджера памяти SQL Server , на лицо ужасного, доброго внутри.

Если вы усвоили что-то из сказанного, я буду просто счастлив.

Взгляд на механизмы Windows Virtual Memory

Как я и обещал в прошлый раз, вот еще одно сообщение о памяти. :) Как вы помните, моя конечная цель – показать, как работает управление памятью в SQL Server, но чтобы вы реально оценили это, думаю, надо, чтобы вы поняли, как управляет памятью Windows. Понимание деталей – это здорово, но пока что я хочу, чтобы вы поняли концепции! В некоторых случаях я намеренно пропускаю детали, так как они на данный момент несущественны, а многие из них могут изменяться от версии к версии. Продолжим.

В предыдущем сообщении я упоминал, что регионы VAS могут быть привязаны к физической памяти сразу или потом с помощью VirtualAlloc API. Как и большинство ОС, Windows привязывает физическую память к VAS при первом обращении к странице в регионе (при работе без файла подкачки все несколько иначе, тогда страница VAS привязывается к физической странице сразу же).

Обратите внимание на эту особенность. Из нее можно сделать следующий вывод: выделение и фиксация (commit) большого объема памяти не приводит к фактическому расходованию физической памяти в системе до первого обращения к выделенной памяти. Причем только реально используемые страницы будут сопоставлены с физической памятью. Это очень важно осознать для правильного понимания алгоритмов выделения памяти, используемых в GC или серверах приложений. – прим.ред.

Привязывается только одна страница за раз. При первом обращении к памяти процессор генерирует исключение, то есть ошибку отсутствия страницы (page fault). Это исключение обрабатывается Windows. ОС проверяет, фиксирован ли текущий регион VAS, изучая структуру VAD, представляющую этот регион. Если регион фиксирован, и если к нему обращаются впервые, ОС найдет физическую страницу RAM, которую она может использовать (помните, что эта страница обнуляется перед использованием по соображениям безопасности). Наконец, она привязывает к странице регион VAS, заполняя соответствующие структуры данных, и загружает эту информацию в CPU, после чего исполнение продолжается с той точки, где произошло исключение.

Из-за нехватки RAM Windows может решить отнять у процесса физическую страницу. Используя политику утилизации, ОС отыскивает страницу, которую будет освобождать. Затем ОС убеждается, что файл подкачки содержит на диске текущую версию страницы, при необходимости сбрасывая страницу на диск.

Автор недаром говорит о том, что ОС только проверяет наличие страницы в файле подкачки. Физически сброс страниц, содержащих данные, на диск ОС старается выполнять во время простоя. Это можно наблюдать невооруженным взглядом, если машину оставить в покое на некоторое время, можно заметить, что она начинает что-то писать на диск. – прим.ред.

Когда страница окажется на диске, ОС сможет исправить все необходимые структуры данных, так что при следующем обращении к странице будет известно, где ее искать. После этого физическая страница обнуляется и помещается в список свободных страниц.

Как я уже говорил, CPU генерирует исключение, page fault, если не может найти физическую страницу, соответствующую виртуальному адресу, к которому пытается обратиться. В приложении это исключение может возникнуть в двух случаях: при первом обращении к региону VAS, или если ОС скинула эту страницу на диск (если приложение обращается к еще не фиксированной странице VAS, будет сгенерировано другое исключение). Тип сбоя, при котором страницу надо извлекать с диска, называется hard page fault. При первом обращении приложения к только что зафиксированному региону VAS будет сгенерировано так называемое исключение «demand zero page fault» (которое приводит к обнулению страницы и выделению ее приложению, это очень быстрая операция – прим.ред.). Есть еще один тип ошибок страницы – soft page fault. Это случается, когда запрашиваемая Windows страница виртуальной памяти может быть найдена без обращения к файлу подкачки.

Механизм виртуальной памяти Windows позволяет отобразить одни и те же физические страницы на разные VAS много раз и в разных местах. Физические страницы, которые могут быть отображены только на один VAS, называются частными (private), поскольку их не могут использовать несколько VAS. Физические страницы, которые могут быть отображены на несколько VAS, называются разделяемыми (shared).

Все регионы VAS, фиксированные с помощью функций VirtualAlloc*, привязываются к физическим страницам, которые не могут совместно использоваться несколькими VAS. Можно считать их частными физическими страницами. Сумма всех частных физических страниц в памяти и на диске называется Private Bytes, если использовать терминологию perfmon, или VM Size, в терминологии Task Manager. Сумма всех частных физических страниц в RAM называется рабочим набором (working set) или Memory Usage, согласно Task Manager.

Как уже говорилось, в зависимости от потребности в физической памяти Windows может удалить физические страницы из рабочего набора процесса. Такая активность обычно называется paging. ОС позволяет избежать этого для региона VAS. Она предоставляет механизм «блокировки» регионов VAS в физической RAM. Как вы можете предполагать, приложение, пытающееся блокировать свои регионы VAS, может дестабилизировать поведение целой системы. Чтобы смягчить такое поведение, в Windows есть привилегия “Lock pages in memory”, по умолчанию выключенная, так что блокировать страницы памяти могут только приложения, получившие на это особое разрешение от администратора. Кроме того, ОС по-прежнему может скинуть на диск рабочий набор целого процесса при необходимости.

Даже при размере регистров x86 в 32 бита платформа разрешает ОС манипулировать 64GB RAM, используя Physical Address Extensions, PAE. Под Windows PAE можно включить в файле boot.ini. Если PAE выключено, Windows может работать только с 4GB RAM, даже если на машине будет установлено больше памяти.

Некоторым приложениям нужно больше VAS, чем 2GB. В Windows есть возможность настроить VAS для пользовательского приложения на 3GB. У этой возможности есть существенный недостаток. Она ограничивает размер VAS, доступного ядру, до 1GB. Увеличить размер VAS для пользовательского приложения можно, добавив ключ /3GB в файл boot.ini. Это изменение требует перезагрузки системы.

Ограничение VAS ядра до 1GB влияет на всю машину, а не только на приложение, нуждающееся в увеличении VAS. Например, при включенном ключе 3GB количество RAM, поддерживаемой операционной системой, падает с 64GB до 16GB. Ключ 3GB влияет на все компоненты ядра, включая все драйверы. Использование этого ключа может вызывать разные эффекты – от падения производительности и сбоев в распределении памяти до зависания системы. Я предлагаю не использовать ключ 3GB, если только вам это не нужно позарез.

Итак, на платформах x86 VAS ограничен до 2GB или 3GB, в зависимости от конфигурации файла boot.ini. На AMD64 32-битным приложениям доступно до 4GB. Такой маленький VAS может быть существенным недостатком для мощных серверов, ворочающих гигабайтами и терабайтами данных, например, SQL Server. Я говорил уже, что ключ PAE позволяет Windows манипулировать 64GB RAM. Но как отдельный процесс может обращаться к такому объему памяти? Чтобы позволить отдельному процессу работать с объемом памяти, превышающим размер VAS, Windows предоставляет механизм Address Window Extension, AWE (помните DOS?). Будьте внимательны – многие авторы используют термин AWE-память. Такой вещи, как AWE-память, не существует, нельзя пойти и купить ее в магазине. А поскольку никакой AWE-памяти нет, не существует low- или high AWE-памяти. Есть только механизм AWE, позволяющий работать с объемом RAM, превышающим VAS! Принцип прост. Используя AWE API, можно занять физическую память, затем с помощью VirtualAlloc API разместить регионы VAS, а затем, используя механизм AWE, связывать/освобождать регионы VAS и физическую память. Как и в случае блокировки страниц, чтобы приложение использовало механизм AWE, нужно включить привилегию «Locked pages in memory».

AWE API можно использовать даже на машинах с RAM, не превышающим VAS процесса. На самом деле, AWE можно использовать, чтобы исключить paging (помните, что при блокировке страниц в памяти с помощью VirtualLock Windows по-прежнему может скинуть на диск весь процесс). При использовании механизма AWE ОС вообще не может вмешаться. Но с этой гибкостью появляется сложность. Ошибочное использование механизма AWE может привести к зависанию машины, и единственным способом выхода из этой ситуации будет перезагрузка.

Думаю, на сегодня достаточно. Все, чего я хочу добиться этим и предыдущим сообщениями – это чтобы вы усвоили концепции. Это действительно важно! Вот краткое резюме:

Несколько интересных деталей:

В следующий раз мы поговорим о memory pressure, и после этого будем готовы погрузиться в SQL Server Memory Manager.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.

Copyright © 1994-2016 ООО "К-Пресс"