В моей первой публикации из серии «Преодолевая ограничения Windows» я рассказал об ограничениях, накладываемых на физическую память, включая ограничения, связанные с лицензированием, реализацией и совместимостью драйверов. В этот раз я решил обратить ваше внимание на другой фундаментальный системный ресурс – виртуальную память. Виртуальная память призвана в некотором смысле отвлечь внимание приложения от физической памяти, при этом операционная система должна решить, когда сохранять код и иную информацию в физической памяти (и стоит ли это делать вообще) и когда стоит сохранить их в файл. Главное преимущество виртуальной памяти заключается в том, что она позволяет одновременно выполняться большому количеству процессов, которые все сразу не смогли бы поместиться в физической памяти.
Помимо существования ограничений для виртуальной памяти, связанных с ограничениями памяти физической, для первой существую также пределы, которые происходят из различных источников и разнятся в зависимости от каждого конкретного потребителя. Например, есть ограничения виртуальной памяти, которые относятся к отдельным процессам, запускаемым приложениями, операционной системой или всей системой в целом. Вам, читающим эту статью, важно помнить, что виртуальная память, как ясно из названия, не имеет какой-либо прямой связи с физической памятью. Windows, выделяя под кэш файла определенное количество виртуальной памяти, не диктует, какой объем данных должен быть размещен в физической памяти; он может быть как равен нулю, так и превышать объем, адресуемый через виртуальную память.
Адресное пространство процессов
У каждого процесса есть своя собственная виртуальная память, именуемая адресным пространством, в которой исполняется код этот процесса и его данные, на которые этот код ссылается и которыми управляет. 32-битные процессы используют 32-битные указатели на адреса в виртуальной памяти, которые создают абсолютный верхний предел в 4 ГБ (2 в 32-ой степени) на объем виртуальной памяти, которую 32-битный процесс может адресовать. Однако, чтобы операционная система могла обратиться к своему собственному коду и данным и к коду и данным, выполняющегося в настоящее время процесса, без необходимости изменять адресное пространство, она делает свою виртуальную память видимой из адресных пространств всех процессов. По умолчанию 32-битная версия Windows разделяет адресное пространство процесса поровну между системой и активным процессом, создавая границу в 2 Гб для каждого:
Приложения могли бы использовать для распределения виртуальной памяти Heap API, сборщик мусора .Net или Malloc-библиотеку C, но все они так или иначе опираются на VirtualAlloc API. Когда приложение исчерпывает свое адресное пространство, VirtualAlloc, а, следовательно, и диспетчеры памяти, построенные на его основе, возвращают ошибки (представленные адресом NULL). Утилита Testlimit, которую я написал для четвертого издания Windows Internals для того, чтобы продемонстрировать различные ограничения Windows, многократно вызывает VirtualAlloc до тех пор, пока не получит ошибку, вызванную указанием параметра -r. Таким образом, когда вы запускаете 32-битную версию Testlimit на 32-битной Windows, она займет все 2 Гб своего адресного пространства:
2010 Мб – это чуть меньше 2-х гигабайт, но наличие в памяти кодов и данных Testlimit, включая исполнительные и системные DLL, объясняют эту разницу. В графе Virtual Size утилиты Process Explorer вы можете увидеть общий объем адресного пространства программы:
Некоторые приложения, такие как SQL Server и Active Directory, управляют большими структурами данных, объем которых намного превышает доступное для них адресное пространство. Поэтому в Windows NT 4 SP3 ввели загрузочную опцию /3GB, позволяющую предоставить процессу 3 Гб из его четырех гигабайтного адресного пространства, уменьшая системное адресной пространство до 1 Гб, а в Windows XP и Windows Server 2003 встроили функцию /userva, которая перемещает границу разделения в пределах с 2 до 3 Гб:
Однако, чтобы использовать адресное пространство выше отметки 2 Гб, в исполняемом образе приложения обязательно должен содержаться набор флагов «large address space aware». Доступ к дополнительной виртуальной памяти является опциональным, потому что некоторые приложения рассчитаны на то, что им будет предоставлено не более 2 Гб адресного пространства. Так как старший разряд указателя, ссылающегося на адрес ниже отметки 2 Гб, всегда является нулем, они использовали бы его как флаг для их собственных данных, очищая его прежде, чем ссылаться на данные. Если бы такие приложения работали в 3-х гигабайтном адресном пространстве, они бы отсекли указатели, значения которых находятся выше 2 Гб, вызывая тем самым ошибки, в том числе и возможное нарушение целостности данных.
Все серверные продукты Microsoft и приложения Windows, интенсивно использующие память, имеют поддержку специального флага, позволяющего использовать адресное пространство выше отметки 2 ГБ; среди таких приложений Chkdsk.exe, Lsass.exe (которая являются главными сервисами Active Directory на контроллере домена), Smss.exe (менеджер сессий) и Esentutl.exe (инструмент восстановления базы данных Jet Active Directory). При помощи утилиты Dumpbin, входящей в состав Visual Studio, вы можете увидеть, у образов каких приложений есть этот флаг:
Testlimit также имеет этот флаг, так что если вы запустите его с параметром -r в 3-х гигабайтном пользовательском адресном пространстве, вы увидите что-то наподобие этого:
Поскольку адресное пространство в 64-битных Windows намного больше 4 Гб, такие версии Windows могут дать 32-битным процессам максимальные 4 Гб, которые они могут адресовать, и использовать оставшуюся виртуальную память для нужд операционной системы. Если вы запустите Testlimit на 64-битной Windows, то увидите, что она занимает все адресное пространство, к которому могут обратиться 32-разрядные приложения:
64-битные процессы используют 64-битные указатели, так что их теоретическое максимальное адресное пространство равно 16 экзабайтам (2 в 64-ой степени). Однако, Windows не делит адресное пространство равномерно между активными процессами и системой, а вместо этого определяет область в адресном пространстве для процессов и других системных ресурсов памяти, таких как системные записи таблицы страниц (PTE), файловые кэши, резидентный и нерезидентный (paged и non-paged) пулы.
Размеры адресного пространства процессов отличаются в IA64 и x64-версиях Windows, где он определяется так, чтобы соблюдался баланс между тем, сколько памяти требуется приложению, и издержками верхней памяти (страницы таблицы страниц и записи буфера трансляции адресов TLB), необходимых для поддержания адресного пространства. Для x64 это 8192 Гб (8 Тб), а для IA64 – 7168 Гб (7Тб) (разница в 1 Тб объясняется тем, что каталоги страниц верхнего уровня в IA64 резервируют слоты для распределений Wow64). И в IA64- и в x64-версиях Windows размер областей адресного пространства для различных ресурсов составляет 128 Гб (например, для нерезидентного пула отводится 128 Гб адресного пространства), за исключением кэша файла, которому выделяется 1 Тб. Поэтому адресное пространство 64-битного процесса выглядит примерно вот так:
На этом рисунке не соблюден масштаб, поскольку в ином случае даже 8 Тб, не говоря уже о 128 Гб, выглядели бы на нем тоненькой полоской. Я использовал этот рисунок, чтобы показать вам, что, как и в случае с нашей вселенной, в адресном пространстве 64-битного процесса есть много незанятного места.
Когда вы запустите 64-битную версию Testlimit (Testlimit64) на 64-битной Windows с параметром -r, вы увидите, что она занимает 8 Тб, которые являются той частью адресного пространства, которыми она может управлять:
Выделенная память
Параметр -r в утилите Testlimit резервирует виртуальную память, но не выделяет ее. Зарезервированная виртуальная память не может хранить данные или код, но приложения иногда используют резервирование для создания больших блоков виртуальной памяти, которую впоследствии они могут выделять, когда необходимо, чтобы выделенная память находилась в смежной с адресным пространством области. Когда процесс выделяет область в виртуальной памяти, операционная система гарантирует, что может предоставить для размещения всех данных процесса область или в физической памяти, или на диске. Это означает, что процесс может столкнуться с еще одним видом ограничений: ограничением на объем выделяемой памяти.
Из вышеизложенного следует, что граница у объема выделяемой памяти равна сумме физической памяти и размера файла подкачки. В действительности в определении границы учитывается не вся физическая память, потому как ее часть операционная система резервирует для своих нужд. Объем выделенной виртуальной памяти для всех активных процессов, называемый текущей выделенной памятью (current commit charge), не может превысить предела, установленного системой. Когда этот предел достигнут, службы, занимающиеся выделением виртуальной памяти, выдают ошибку. Это означает, что даже стандартный 32-битный процесс может столкнуться с ошибкой выделения виртуальной памяти еще до того, как он достигнет предела на объем виртуального адресного пространства в 2 Гб.
Текущий объем выделенной памяти и ее ограничение можно посмотреть в окне System Information утилиты Process Explorer в разделе Commit Charge и на графике Commit History:
Диспетчер задач в системах до Vista и Windows Server 2008 точно показывает эти параметры, но там текущий объем выделенной памяти на графике назван «PF Usage»:
В Vista и Server 2008 диспетчер задач не показывает график текущей выделенной памяти, а отображает эти два значения в строке Page File (даже если вы отключите файл подкачки, эти значения будут ненулевыми):
Чтобы проверить значение границы выделенной памяти, вы можете запустить Testlimit с параметром -m, чтобы программа распределила выделенную память. 32-битная версия Testlimit может и не достигнуть предела своего адресного пространства до того, как достигнет предела на выделенную память; это зависит от объема физической памяти, размера файла подкачки и значения текущей выделенной памяти на момент запуска программы. Если вы используете 32-битную версию Windows и хотите увидеть, как поведет себя система при достижении предела на выделенную память, просто запустите несколько экземпляров Testlimit до тех пор, пока один из них не достигнет предела выделенной памяти до того, как исчерпает свое адресное пространство.
Здесь следует отметить, что по умолчанию файл подкачки настроен так, чтобы увеличиваться в размерах. Это означает, что предел для выделенной памяти также будет расти по мере приближения к нему значения выделенной памяти. И даже тогда, когда файл подкачки достигнет своего максимального размера, Windows может освободить еще немного памяти, которую она зарезервировала, аналогично тому, как некоторые приложения создают кэш данных. Testlimit знает об этом, и когда она достигает предела для выделенной памяти, приложение несколько секунд бездействует, а затем пытается получить еще немного памяти. Приложение будет повторять попытки до тех пор, пока вы его не отключите.
Если вы запустите 64-битную версию Testlimit, то она почти наверняка достигнет предела для выделенной памяти до того, как исчерпает свое адресное пространство, если, конечно, размеры файлов подкачки и физической памяти не дают в сумме более 8 Тб, которые, как описано ранее, являются пределом 64-битного адресного пространства, доступного приложению. Вот фрагмент результатов работы 64-битной версии Testlimit, запущенной на моей системе с 8Гб памяти (я указал размер на выделяемую память в 100 Мб, чтобы быстрее достигнуть предела):
А вот график для выделяемой памяти, на котором шаги соответствуют паузам в работе Testlimit, сделанным для того, чтобы файл подкачки смог увеличиться:
Когда у системы остается мало виртуальной памяти, приложения могут зависать и вы можете увидеть странные сообщения об ошибке, когда выполняете стандартные операции. Тем не менее, в большинстве случаев Windows выводит диалоговое окно, сообщающее о недостатке виртуальной памяти, наподобие того, которое система показала мне во время проведения теста:
После того, как вы закроете Testlimit, ограничение на выделенную память, скорее всего, вернется на прежний уровень, когда диспетчер памяти уменьшит размер файла подкачки. На следующем снимке экрана Process Explorer показывает, что предел для выделенной памяти находится ниже того пика, который был достигнут во время работы Testlimit:
Память, выделяемая процессу
Поскольку предел для выделенной памяти является общим показателем для всей системы, достижение которого может привести к общему снижению производительности, ошибкам приложений или даже к зависанию системы, вполне логичным выглядит вопрос «сколько выделенной памяти должно быть выделено процессу»? Чтобы точно ответить на этот вопрос, вам нужно знать, какие еще типы виртуальной памяти могут быть заняты процессом.
Не вся виртуальная память, которой владеет процесс, имеет отношение к пределу выделенной памяти. Как вы уже знаете, примером здесь может служить зарезервированная виртуальная память. Виртуальная память, которая представляет файл на диске (такое представление носит название File Mapping View), также не участвует в расчете этого предела, за исключением случаев, когда приложение требует семантики copy-on-write, потому что Windows может стереть любые данные, связанные с образами в физической памяти, а после восстановить их из этого файла. Поэтому виртуальная память из адресного пространства Testlimit, в которую отображаются ее исполнительные и системные DLL, не учитывается при расчете предела выделенной памяти. Есть два типа виртуальной памяти процесса, которые учитываются при определении границы выделенной памяти: эти области называются «private» и «pagefile-backed».
Закрытая виртуальная память является тем видом виртуальной памяти, на основе которого строятся Garbage Collector Heap, Native Heap и Language Allocator. Она называется закрытой, потому что к ней не может быть открыт общий доступ для других процессов. По этой причине ее легко приписать к процессу, а Windows может отслеживать ее использование с помощью счетчика Private Bytes. Process Explorer отображает объем используемой закрытой памяти процесса в графе Private Bytes секции Virtual Memory на страницу Performance диалогового окна свойств процесса, а также отображает эти показатели в графическом виде на странице Performance Graph. Вот как выглядел Testlimit64 после достижения предела выделенной памяти:
Виртуальную память Pagefile-backed сложно отнести к конкретному процессу, потому что она может одновременно использоваться несколькими процессами. Фактически, нет никакого счетчика, привязанного к одному конкретному процессу, позволяющего посмотреть, сколько памяти этому процессу выделено или на какой объем этой памяти он ссылается. Когда вы запускаете утилиту Testlimit с параметром -s, она начинает занимать участки виртуальной памяти pagefile-backed до тех пор, пока не достигнет предела для выделенной памяти, но даже когда объем выделенной памяти превысит 29 Гб, статистика потребления виртуальной памяти для данного процесса не будет содержать ни одного указателя на то, что он один ответственен за это:
Именно по этой причине в программу Handle я добавил параметр -l. Процесс должен открыть объект в виртуальной памяти pagefile-backed, носящий название секции, для создания отображения виртуальной памяти pagefile-backed в его адресном пространстве. Так как Windows сохраняет существующую виртуальную память, даже если приложение закроет дескриптор к секции, из которой он был создан, большинство приложений сохранят этот дескриптор открытым. Параметр -l отображает размеры областей для секций pagefile-backed, которые открыты процессами. Вот часть результатов работы Handle для утилиты Testlimit, после того, как она была запущена с параметром -s:
Как видите, Testlimit занимает память pagefile-backed блоками по 1 Мб, и если просуммировать размер всех блоков, открытых этой программой, можно увидеть, что она является одним из процессов, занявших наибольший объем выделенной памяти.
Насколько большим следует сделать файл подкачки?
Возможно, наиболее частым вопросом, связанным с виртуальной памятью, является вопрос «насколько большим следует сделать файл подкачки?». Ни в сети, ни в информационных изданиях, освещающих вопросы Windows, нет конкретного ответа на этот вопрос, и даже Microsoft опубликовала на этот счет довольно запутанные рекомендации. Почти все советы основаны на умножении объема оперативной памяти на некоторое значение, например, на 1.2, 1.5 или 2. Теперь, когда вы понимаете, какую-роль играет файл подкачки в определении системного лимита на выделенную память и как процессы влияют на объем выделенной памяти, мы легко можете увидеть, насколько бесполезны такие формулы в действительности.
Так как предел для выделенной памяти устанавливается на основе того, сколько закрытой и pagefile-backed виртуальной памяти может быть одновременно выделено выполняющимся процессам, единственный способ правильно установить размер файла подкачки заключается в том, чтобы узнать, какой максимальный объем выделенной памяти может быть занят программами, которые вы часто запускаете одновременно. Если предел для выделенной памяти будет меньше этого значения, то ваши программы не смогут получить необходимую им виртуальную память и будут некорректно работать.
Так как же узнать, сколько выделенной памяти требуется вашей рабочей среде? На снимках экрана вы, возможно, заметили, что это число отслеживается Windows и Process Explorer показывает его: Peak Commit Charge. Чтобы установить оптимальный размер для вашего файла подкачки, вы должны запустить все приложения, с которыми вы работаете одновременно, загрузить типичный для вас объем данных и посмотреть пиковое значения выделенной памяти (или же посмотреть это значение по прошествии некоторого времени, когда, по вашему мнению, будет достигнута максимальная загруженность памяти). В качестве минимального размера файла подкачки установите это значение, отняв от него размер установленной на вашей системе оперативной памяти (если получиться отрицательное значения, выберите размер, позволяющий сохранить возможное количество отказов на минимальном уровне). Если вы хотите сохранить некоторый запас для потенциально больших расходов выделенной памяти, в качестве максимума установить это значение, умноженное на 2.
У вас может сложиться впечатление, что отсутствие файла подкачки может благотворно сказаться на производительности, однако в общем случае то, что у Windows в распоряжении будет файл подкачки, означает, что ОС сможет размещать некоторые записи (которые используются нечасто и не сохранены на диск) в файл подкачки, освобождая тем самым память для более полезных задач (процессы и кэши файлов). Так что даже если в некоторых случаях отсутствие файла подкачки может увеличить производительность, в общем случае его наличие означает, что в распоряжении системы будет больше доступной памяти (Windows в случае сбоя не сможет сделать дамп памяти, занятой под процессы ядра, если в ее распоряжении не будет достаточно большого файла подкачки).
Настройки файла подкачки находятся в окне Свойства системы, которое вы можете открыть, введя в окне Выполнить строчку «sysdm.cpl», после этого, зайдя на вкладку Дополнительно, нажать кнопку Параметры раздела Быстродействие, затем на вкладке Дополнительно нажать кнопку “Изменить”:
Там вы можете заметить, что по умолчанию Windows сама управляет размером файла подкачки. Когда это опция установлена в Windows XP и Server 2003, ОС создает один файл подкачки, минимальный размер которого равен 1,5 объема ОП системы; если объем оперативной памяти больше 1 Гб, то такой же размер устанавливается для файла подкачки; максимальный размер файла подкачки равен трем объема ОП. В Windows Vista и Server 2008 минимальное значения должно быть достаточно для того, чтобы в случае сбоя системы сделать дамп памяти, занятой под процессы ядра, и равно ОП+300 Мб или 1 Гб, в зависимости от того, какое значение окажется больше. Максимально значение – три объема оперативной памяти или 4 Гб, в зависимости от того, что окажется больше. Это объясняет тот факт, что на моей 64-битной системе с 8 Гб ОП пиковое значения выделенной памяти равно 32 Гб.
Другими ограничениями, связанными с виртуальной памятью Windows, являются максимальный размер и количество файлов подкачки. Для 32-битной Windows максимальный размер файла подкачки равен 16 Тб (4 Гб, если вы по какой-то причине работаете в режиме non-PAE), а 64-битная Windows может иметь файл подкачки размером до 16 Тб для x64 и 32 Тб в случае IA64. Для всех версий Windows есть возможность создавать до 16 файлов подкачки, каждый из которых должен располагаться на разных томах.
Оригинал записи
Это перевод Pushing the Limits of Windows: Physical Memory. Автор: Марк Руссинович.
Это первый пост в блоге из серии «Pushing the Limits of Windows», которую я буду писать ближайшие месяцы и в которой буду описывать как Windows и приложения используют конкретный ресурс, лицензионные и реализационные ограничения ресурса, как измерить использование ресурса и как диагностировать его утечки. Чтобы эффективно управлять своими Windows системами вам нужно понимать как Windows управляет физическими ресурсами, такими как процессоры (CPUs) и память (memory), а также логическими ресурсами, такими как виртуальная память (virtual memory), дескрипторы (handles) и объекты оконного менеджера (window manager objects). Знание пределов и ограничений этих ресурсов и методы слежения за ними позволит вам соотносить использование ресурсов с приложениями, которые их используют, эффективно изменять систему для определённой нагрузки и идентифицировать приложения с утечкой ресурсов.
Физическая память
Физическая память является одним из основных ресурсов компьютера. Менеджер памяти Windows является ответственным за заполнение памяти кодом и данными запущенных процессов, драйверов устройств и самой операционной системы. Поскольку большинство систем работают с большим объёмом кода и данных, чем может вместить в себя физическая память компьютера, то физическая память, по сути, является окном в используемые код и данные. Поэтому количество установленной памяти влияет на производительность, потому что если данные или код не присутствуют в физической памяти, то менеджеру памяти необходимо загрузить их с диска.
Кроме влияния на производительность, количество установленной физической памяти привносит и другие ограничения. Например, размер не подкачиваемого пула (non-paged pool) — буфера операционной системы — очевидно, ограничен физической памятью. Физическая память также влияет на ограничения системной виртуальной памяти, размер которой является грубой суммой физической памяти плюс максимальным размером всех файлов подкачки. Ещё Физическая память может неявно влиять на максимальное число одновременно работающих процессов, о чём я буду говорить в будущем посте, посвящённому ограничениям на процессы и потоки.
Ограничения памяти на серверных Windows
Поддержка физической памяти Windows диктуется ограничениями аппаратной части, лицензированием, структурами операционной системы и совместимостью драйверов. Страница Memory Limits for Windows Releases в MSDN описывает ограничения в различных версиях Windows, а также в пределах версий по редакциям (SKU).
Вы можете увидеть различия в поддержки памяти у разных редакций Windows, диктуемые лицензионными ограничениями. Например, 32-х битная Windows Server 2008 Standard поддерживает только 4 Гб, в то время как 32-х битная же Windows Server 2008 Datacenter поддерживает уже 64 Гб. Аналогично, 64-х битная Windows Server 2008 Standard поддерживает 32 Гб, а 64-х битная Windows Server 2008 Datacenter может оперировать целыми 2 Тб. Вокруг нас не так много систем с 2 Тб физической памяти на борту, но команда производительности Windows Server (Windows Server Performance Team) знает парочку, включая одну, которая была у них в лаборатории для тестов. Вот скриншот Менеджера Задач (Task Manager), работающего на такой системе:
Максимальное ограничение в 128 Гб на 32-х битных Windows, поддерживаемое редакцией Windows Server 2003 Datacenter, следует из того, что структуры, используемые менеджером памяти для отслеживания физической памяти, занимали бы слишком много виртуального адресного пространства на системах с большим количеством памяти. Менеджер памяти следит за каждой страницей (page) памяти, храня данные о ней в массиве, называемом базой данных PFN (PFN database), и (по соображениям производительности) проецируя всю базу данных PFN в виртуальную память. Поскольку в ней каждая страница физической памяти представлена структурой данных размером 28 байт, то вся БД PFN для системы с 128 Гб занимает около 930 Мб. 32-х битные Windows имеют 4 Гб виртуального адресного пространства, которое аппаратно делится на две части: пользовательскую, в котором выполняется процесс пользователя (например, Блокнот), и системную. Поэтому 980 Мб занимают практически половину их доступных 2 Гб из системной части виртуального адресного пространства, оставляя только 1 Гб для ядра, драйверов устройств, системного кэша и других системных структур данных, что делает совершенно неразумным дальнейшее увеличение размера БД:
По этой же причине в таблице с ограничениями также указываются пределы для той же редакции, но при загрузке с модификацией этих 4 Гб (опция называется 4GT и включается указанием в Boot.ini /3GB или /USERVA, а также опцией /Set IncreaseUserVa в Bcdedit), потому что опция 4GT передвигает границу между пользовательской и системной частью виртуального адресного пространства, что даёт до 3 Гб пользовательской части, но оставляет только 1 Гб для системы. Для улучшения производительности, 32-х битный Windows Server 2008 увеличивает зарезервированную часть для системного адресного пространства понижением максимального размера поддерживаемой памяти до 64 Гб.
Менеджер памяти мог бы поддерживать и больше памяти, проецируя БД PFN по кускам в адресное пространство по мере необходимости, но это добавило бы сложности и уменьшило производительность за счёт добавленных накладных расходов на операции проецирования и снятия проекции (map and unmap operations). Только недавно системы стали настолько большими, что нужно задумываться об этом, но поскольку размер адресного пространства не является ограничением для БД PFN на 64-х битных Windows, то поддержка большего количества памяти перекладывается на 64-х битные Windows.
Максимальный предел в 2 Тб на 64-х битном Windows Server 2008 Datacenter не следует из каких-либо ограничений реализации или аппаратной части, но Microsoft поддерживает только те конфигурации, который она может протестировать. Ко времени релиза Windows Server 2008, самая большая система в мире имела 2 Тб, поэтому Windows ограничивает свою поддержку памяти именно 2-мя Тб.
Ограничения памяти на клиентских Windows
64-ти битные клиентские редакции Windows поддерживают различное количество физической памяти как средство усиления различий между редакциями (SKU-differentiating feature) — от нижнего предела в 512 Мб для Windows XP Starter до 128 Гб для Vista Ultimate. Все 32-х битные клиентские редакции Windows, однако, (включая Windows Vista, Windows XP и Windows 2000 Professional) поддерживают максимум 4 Гб физической памяти. 4 Гб — это максимальный размер физической памяти, доступный из стандартного режима управления памятью на x86. В те времена не было необходимости даже рассматривать большее число памяти на клиентских системах, потому что даже сервера с таким количеством памяти были редкостью.
Однако, ко времени, когда Windows XP SP2 был в разработке, клиентские системы с более чем 4 Гб памяти появились в обозримом будущем, поэтому команда Windows начала широкомасштабное тестирование Windows XP на системах с памятью более 4 Гб. Windows XP SP2 также включал по-умолчанию поддержку Physical Address Extensions (PAE) на железе, которое реализовывало память no-execute, потому что оно требовалось для Data Execution Prevention (DEP), но оно же включало поддержку для памяти выше 4 Гб.
Но при этом разработчики Windows обнаружили, что многие системы начинали вылетать, зависать или вообще не загружаться, потому что некоторые драйвера устройств (часто это такие видео или аудио драйвера, которые обычно используются на клиентских системах, а не на серверах) не были запрограммированы работать с физическими адресами больше 4 Гб. В результате, драйвера обрезали такие адреса, приводя к порче памяти и побочным эффектам такой порчи. Серверные системы обычно имеют более общие устройства с простыми и более стабильными драйверами, а поэтому обычно не встречаются с подобными проблемами. Эта проблематичная экосистема клиентских драйверов привела к принятию решения об игнорировании памяти выше 4 Гб на клиентских редакциях, даже если система теоретически может адресовать столько памяти.
Реальные ограничения памяти на клиентских 32-х битных Windows
Хотя 4 Гб памяти заявлено как предел для 32-х битных редакций Windows, но реальный предел будет ниже и зависит от чипсета системы и подключенных устройств. Причина в том, что физическое адресное пространство включает в себя не только оперативную память (RAM), но также и память устройств, а системы x86 и x64 проецируют память устройств ниже границы в 4 Гб для обеспечения совместимости с 32-х битными операционными системами, которые не знают, как обращаться с адресами выше 4 Гб. Если система имеет 4 Гб оперативной памяти и устройства, типа видео, аудио или сетевых адаптеров, будут реализовывать окна в свою память (которая в сумме пусть будет равна 500 Мб), то 500 Мб памяти из 4-х Гб будут расположены выше границы в 4 Гб, как показано ниже:
В результате этого, если у вас есть система с 3 Гб или более памяти под управлением клиентской версии 32-х битной Windows, вы не сможете получить в своё распоряжение всю оперативную память. На Windows 2000, Windows XP и Windows Vista RTM вы можете узнать, сколько оперативной памяти доступно системе в диалоговом окне Свойства системы, странице Производительность Менеджера Задач и, в Windows XP и Windows Vista (включая SP1), в утилитах Msinfo32 и Winver. На Window Vista SP1 некоторые из этих мест теперь показывают установленную память, а не доступную для системы память, как указано в этой статье Knowledge Base.
На моём ноутбуке с 4 Гб памяти, когда я загружаю 32-х битную Vista, количество доступной физической памяти будет 3.5 Гб, как показано в утилите Msinfo32:
Вы можете посмотреть раскладку физической памяти с помощью утилиты Meminfo от Alex Ionescu (он также внёс вклад в 5-ю редакцию Windows Internals, которую я написал в соавторстве с Дэвидом Соломоном). Вот вывод Meminfo, которую я запустил на этой же системе с ключом -r для создания дампа диапазонов физической памяти:
Обратите внимание на пробел в адресном пространстве со страницы 9F000 до страницы 100000, и ещё один от DFE6D000 до FFFFFFFF (4 Гб). Однако, когда я загружаюсь на этой же системе в 64-х битной Vista, все 4 Гб показываются как доступные и вы можете видеть, как Windows использует ещё 500 Мб оперативной памяти, находящиеся выше границы в 4 Гб:
Откуда берутся эти пробелы ниже 4 Гб? На этот вопрос нам поможет ответить Диспетчер Устройств. Для проверки, запустите «devmgmt.msc», выберите «Ресурсы по подключению» в меню «Вид», и разверните узел «Память». На моём ноутбуке главным похитителем памяти стала, что и неудивительно, видеокарта, которая потребляет 256 Мб в диапазоне E0000000-EFFFFFFF:
Прочие девайсы занимают большинство оставшегося места, а шина PCI резервирует дополнительные диапазоны для устройств во время загрузки как часть консервативной оценки firmware этих устройств.
Потребление адресов памяти ниже 4 Гб может быть весьма значительным на топовых игровых конфигурациях, имеющими видеокарты с большими объёмами памяти. Например, я купил одну такую систему в бутике игрового стенда компании — она шла с 4 Гб памяти и двумя видеокартами по 1 Гб каждая. Я не уточнил версию ОС, полагая, что они сообразят поставить 64-х битную Виста, но система пришла с 32-х битной версией. В результате только 2.2 Гб памяти было доступно для Windows. Вы можете увидеть гигантскую дыру в памяти с адреса 8FEF0000 по FFFFFFFF в следующем выводе Meminfo с этой системы после того, как я поставил на неё 64-х битную версию Windows:
Диспетчер Устройств показывает, что 512 Мб из этой дыры в зарезервированно для видеокарт (256 Мб каждая), и похоже, что firmware просто зарезервировало больше памяти, либо для динамического проецирования, либо же в результате использования консервативных оценок:
С такими проблемами могут встретиться даже системы имеющие на борту только 2 Гб. Проблемы возникают из-за чипсетов, агрессивно резервирующих регионы памяти для устройств. Наш общий семейный компьютер, который мы купили всего несколько месяцев назад от известного OEM поставщика, сообщает о доступности только 1.97 Гб из 2-х установленных:
Диапазон физических адресов от 7E700000 до FFFFFFFF зарезервирован шиной PCI и устройствами, что оставляет теоретический максимум физического адресного пространства в 7E700000 байт (1.976 Гб), но даже часть из этого резервируется для памяти устройств, что оставляет Windows только 1.97 Гб.
Поскольку теперь производители устройств обязаны отправлять как 32-х битный, так и 64-х битный драйвера в лаборатории Windows Hardware Quality Laboratories (WHQL) для получения сертификата подписи, то большинство производителей устройств сегодня могли бы поддерживать физические адреса выше 4 Гб. Однако, 32-х битные Windows будут продолжать игнорировать память выше этой границы, потому что всё ещё существуют определённые сложности с измерением риска такого изменения, а производители OEM двигаются (или, по крайней мере, должны) в сторону перехода на 64-х битные Windows, где эта проблема просто не стоит.
Суть заключается в том, что вы можете использовать всю установленную у вас память (до предела, установленного редакцией системы) с 64-х битной Windows, вне зависимости от её количества, и если вы покупаете высокопроизводительную геймерскую систему, то вам определённо стоит попросить продавца установить на неё 64-х битную Windows.
Достаточно ли у вас памяти?
Вне зависимости от того, сколько памяти у вас есть, настоящий вопрос заключается в том, достаточно ли этой памяти? К сожалению, не существует какого-либо простого правила, с помощью которого вы точно узнаете ответ на этот вопрос. Хотя есть общее руководство, которое вы можете использовать, оно основано на мониторинге доступной системе памяти за промежуток времени, особенно в моменты работы приложений, требовательных к памяти. Windows определяет доступную память как физическую память, которая не была привязана к процессу, ядру или драйверу устройства. Как следует из этого названия, доступная память доступна для присвоения её по требованию процесса или системы. Системный менеджер памяти, конечно же, пытается использовать максимум памяти, используя свободную память как файловый кэш (список standby), как обнулённую память (список страниц, заполненных нулями), а функция Superfetch в Vista также использует её для предзагрузки (prefetch) данных и кода в списке standby и упорядочивает их по наибольшей вероятности использования в ближайшее время.
Когда доступная память становится дефицитом, это означает, что либо процессы, либо система активно используют физическую память, и если доступная память находится в районе нуля продолжительное время, то вы вероятно выиграете от установки большего количества памяти. Есть несколько способов отслеживать доступную память. В Windows Vista вы можете неявно следить за доступной памятью, наблюдая за историей использования физической памяти (Physical Memory Usage History) в Диспетчере Задач, Task Manager. Вот снимок экрана Диспетчера Задач на моём настольнике с 8 Гб памяти (хмм, я думаю, что у меня, пожалуй, даже слишком много памяти!):
На всех версиях Windows вы можете увидеть график доступной памяти, используя оснастку Производительность (Performance Monitor) и добавляя в ней счётчик Available Bytes из раздела Memory:
Вы также можете видеть мнгновенные значения в диалоге System Information утилиты Process Explorer, или, на версиях Windows до Vista, на вкладке Производительность Диспетчера Задач (Task Manager’s Performance page).
Преодолевая пределы
Из процессора (CPU), памяти и дисков — память обычно наиболее важна для общей производительности системы. Чем больше памяти — тем лучше. И 64-х битные Windows являются способом убедиться, что вы используете все возможности вашей системы. Кроме того, 64-х битные Windows могут иметь и другие бонусы в плане производительности, о которых я поговорю в следующем посте серии Pushing the Limits, когда я буду говорить о виртуальной памяти.
Этот пост — несколько необычное ответвление (spin-off) предыдущего.
Слова «звучит как сюжет для разрушителей легенд» из него, сказанные случайно, красного словца ради, сильно запали мне в голову, и я решил, что было бы неплохо самому «разрушить» несколько таких мифов. И вот появился этот пост — возможно, недостаточно точный технически, но рассказывающий различные факты о памяти в, как я надеюсь, занимательной форме.
Мне кажется, что будет особенно интересно, если вы попробуете при чтении угадывать результаты экспериментов (и статус мифов) до того, как они будут изложены.
Содержание
- Программа не может выделить больше памяти, чем установлено ОЗУ
- Суммарный размер памяти для всех программ не может превышать
2Гб 32-х разрядное приложение не может выделить1.5Гб памяти за раз32-х разрядное приложение не может использовать более2Гб памяти- Ключ
/3GBрасширяет пользовательское адресное пространство для всех программ - Режим
/3GBпозволит мне выделить1гигантский блок памяти в3Гб - 32-х разрядная программа не может выделить более
3Гб в своём адресном пространстве - 32-х разрядная операционная система не может использовать все
4Гб оперативной памяти - Вам нужно включать режим
/3GB, если у вас есть больше2Гб физической памяти - Большой .exe файл — это плохо, потому что он тратит память
- Delphi приложение занимает много памяти
- Доступ к невыделенной памяти приводит к возбуждению Access Violation
- Освобождение памяти уменьшает показатели использования памяти программы
Obj.Freeне приводит кObj = nil- Если программа не освободит память, то в системе останется мусор и она замедлится
Миф №1: программа не может выделить больше памяти, чем установлено ОЗУ
Миф происходит от того, что люди не понимают, что адресное пространство программы теперь виртуально. Оно более не связано с оперативной памятью (вот уже более пятнадцати лет).
Этот миф легко разрушить непосредственным экспериментом. Я установил количество ОЗУ для виртуальной машины в 256 Мб, запустил её и выполнил такой код:
procedure TForm1.Button1Click(Sender: TObject); begin AllocMem(512 * 1024 * 1024); // выделить 512 Мб памяти end;
Эта операция будет успешна (хотя и достаточно медленна). Операция была бы мгновенной, если бы мы использовали только резервирование (RESERVE), вместо полноценного выделения (COMMIT), но, возможно, тогда эксперимент не был бы таким зрелищным.
Итак, вот снимок экрана с запущенной программой до выделения:
и после (я нажал на кнопку аж два раза):
А вот и общая статистика системы:
Как вы видите, на машине установлено 261'616 Кб оперативной памяти. До выделения памяти наша программа занимала 31'980 Кб виртуальной памяти и 3'764 Кб оперативной. После выделения памяти программа стала занимать 1'080'752 Кб виртуальной памяти и 1'748 Кб физической. Вы также можете увидеть, что суммарное количество выделенной памяти в системе равно 1'313'300 Кб.
Итак, легенда разрушена прямым экспериментом.
Как известно, классический сюжет Mythbusters состоит из двух частей. Когда легенда разрушена, разрушители легенд подбирают такие условия, при которых происходило бы событие, упоминающееся в легенде.
Этим мы сейчас и займёмся: мы заходим в свойства системы и уменьшаем размер файла подкачки до 128 Мб. Таким образом, суммарный объём памяти, доступный системе и всем программам, будет равен 256 + 128 = 384 Мб.
Перезагрузка, запускаем тестовый пример снова и вот результат:
На этот раз наш вызов AllocMem проваливается с выбросом исключения EOutOfMemory. И Process Explorer показывает нам причину:
Статус мифа: busted.
Миф №2: суммарный размер памяти для всех программ не может превышать 2 Гб
Выше мы увидели, что программа может выделить сколько угодно памяти, пока у неё есть место в виртуальном адресном пространстве. Т.е. 32-х разрядная программа может выделить 512 Мб, но не 2 Гб — потому что это размер пользовательской части адресного пространства по умолчанию. Некоторые люди считают, что все запущенные программы в системе не могут выделить более двух гигабайт памяти.
Этот миф происходит от того, что люди не понимают, что адресное пространство программы теперь своё у каждой программы (вот уже более пятнадцати лет).
Посмотрим, так ли это.
В этот раз я запустил пять копий программы-примера из предыдущего пункта и вот что получилось:
А вот и статус системы в целом:
Как видите, никаких проблем нет: все запущенные приложения в системе смогли выделить 2'902'204 Кб памяти (и да, я поднял кол-во ОЗУ до 1 Гб, чтобы система поменьше тормозила).
Что касается части два, то она выглядит так же, как ранее: нельзя выделить памяти больше, чем у вас есть оперативной памяти + файла подкачки.
Статус мифа: busted.
Миф №3: 32-х разрядное приложение не может выделить 1.5 Гб памяти за раз
Несмотря на то, что приложению доступно по умолчанию около 2 Гб виртуального адресного пространства, утверждается, что приложение не может выделить 1.5 Гб памяти одним куском.
Давайте проверим. Изменим код с AllocMem в нашем тестовом приложении на выделение 1.5 Гб и запустим программу. Получаем:
Легенда разрушена?
Не так быстро. Попробуем сделать это на другой машине:
(сообщения «Мало виртуальной памяти» нет)
Гм, в этот раз нам не удаётся выделить 1.5 Гб памяти.
Мы получили противоречивые результаты. В чём же дело?
Хотя нам действительно доступно около 2 Гб одним куском (только в самом начале и в самом конце этого региона откушено по 64 Кб на спец. области), но нужно вспомнить, что в этом адресном пространстве лежат не только ваши данные, но и ваш код, библиотеки (DLL), их код и так далее. Даже если вы не загружали библиотек явно в вашем коде — они всё равно будут загружены. Как минимум это kernel32.dll и user32.dll. И дальше всё зависит от того, как именно они загружены. Обычно системные библиотеки загружаются одним большим компактным регионом, расположенном по старшим адресам — поскольку они загружаются с краю адресного пространства, то в центре у вас получается большой кусок для вашей работы. Но если какая-то DLL загружается в середину адресного пространства, то оно оказывается разбито пополам, и вы уже не сможете выделить память одним куском (но всё ещё можете выделить её в два или три куска).
К примеру, вот снимок загруженных DLL в адресном пространстве первого примера (который успешно выделил 1.5 Гб памяти) до выделения памяти:
Как видим, в центре у нас есть большой свободный кусок — от $2D40000 до $648B0000, т.е. $648B0000 - $2D40000 = 1'563 Мб (примечание: это не значит, что в этом промежутке нет вообще ничего — там могут быть не DLL, а данные). Т.е. у нас есть свободное место.
А вот этот же снимок DLL на машине, где выделить память не удалось:
Как видите, в этом случае в середине большого свободного промежутка у нас разместилась DLL от FileBox Extender — это небольшая утилита, которая добавляет полезные кнопки в заголовки окон. Поскольку она меняет поведение каждого окна, то она должна быть загружена в каждую программу. Но из-за того, что она оказалась неграмотно спроектированной, её базовый адрес оказался в неудачном месте. Такая ситуация называется фрагментацией адресного пространства.
Мораль истории: либо ставьте поменьше «расширителей оболочки», либо следите, чтобы они были грамотно спроектированы.
Статус мифа: plausible.
Миф №4: 32-х разрядное приложение не может использовать более 2 Гб памяти
Постойте-ка, разве мы только что не подтвердили эту легенду? Не совсем. Ведь есть разница: «выделить за раз» и «использовать». Да, вы не можете выделить 2 Гб памяти (или более) — что за раз, что за несколько вызовов: ведь обычно размер пользовательской части виртуального адресного пространства равен 2 Гб, но это не ограничивает вас 2 Гб виртуальной памяти. Вы можете выделять память, без проецирования её в ваше виртуальное адресное пространство. Как мы увидели в мифах 1 и 2: виртуальное адресное пространство программы не равно виртуальной памяти в системе. Второе — больше, чем первое.
Обычно размер пользовательской части виртуального адресного пространства равен 2 Гб, но это не ограничивает вас 2 Гб виртуальной памяти. Вы можете выделять память, без проецирования её в ваше виртуальное адресное пространство:
h := CreateFileMapping(INVALID_HANDLE_VALUE, 0,
PAGE_READWRITE, 1, 0, nil);
При условии, что у вас достаточно физической памяти и/или файла подкачки, этот запрос на выделение 4 Гб памяти будет успешен.
Конечно же, вы не сможете спроецировать всю эту память сразу, но вы можете делать это по частям.
Другим вариантом использования большего объёма памяти является AWE.
В общем, мораль в том, что виртуальное адресное пространство — это не то же самое, что виртуальная память. Как мы видели ранее, вы можете проецировать одну и ту же память по нескольким адресам, так что соответствие один-к-одному для виртуальной памяти и виртуального адресного пространства никогда и не выполнялось. Здесь мы продемонстрировали, что выделение памяти вовсе не означает, что она вообще занимает какое-то место в виртуальном адресном пространстве.
Более того: если вы укажете при загрузке системы ключ /3GB, то вы сможете использовать более 2 Гб виртуального адресного пространства (и снова: и ещё больше — виртуальной памяти). Ключ /3GB изменяет способ разбиения полных 4 Гб виртуального адресного пространства. Вместо разбиения на 2 Гб пользовательского виртуального адресного пространства и 2 Гб режима ядра, разделение будет сделано на 3 Гб пользовательского и 1 Гб адресного пространства режима ядра (это граница по умолчанию, а вообще она варьируется).
Так что это неверно даже если вы говорили про адресное пространство, а не про память вообще. В этом мифе есть лишь часть правды про адресное пространство.
Статус мифа: (totally) busted.
Миф №5: ключ /3GB расширяет пользовательское адресное пространство для всех программ
Ну, давайте включим режим /3GB и запустим нашу программу пример, где AllocMem выделяет 100 Мб. Будем нажимать на кнопку, пока не возникнет сообщение о нехватке памяти и посмотрим, сколько же памяти нам удалось выделить:
Как видим, это существенно меньше ожидаемых 3 Гб памяти.
На самом деле, режим /3GB влияет только на программы с флагом IMAGE_FILE_LARGE_ADDRESS_AWARE.
По соображениям совместимости, только программы, которые явно пометили себя, что они умеют обрабатывать виртуальное адресное пространство больше 2 Гб, получат большее адресное пространство. Не помеченные программы получат свои обычные 2 Гб, а адресное пространство между 2 Гб и 3 Гб не будет использоваться вовсе.
Почему?
Потому что слишком много программ предполагают, что старший бит адреса в пользовательском режиме всегда очищен (т.е. равен 0), часто делая это невольно. В MSDN есть страничка, на которой перечислены несколько способов использования такого предположения. Например, вы можете захотеть найти средний адрес между двумя другими — используя для этого формулу (a + b) / 2. Но если a и b будут больше 2 Гб, то их сумма не влезет в 4-х байтное целое — следовательно, вы получите неверный результат (для верного вычисления надо использовать выражение a + (b - a) / 2). Соответственно, вы не можете просто взять программу, которую вы не писали, пометить её флагом IMAGE_FILE_LARGE_ADDRESS_AWARE и объявить, что дело сделано. Вам вместе с авторами программы надо проверить, что код не делает никаких предположений насчёт этих 2 Гб (а тот факт, что программа не была помечена, как совместимая с 3 Гб, означает, что никаких проверок не было сделано. В самом деле — в противном случае она была бы уже помечена флагом IMAGE_FILE_LARGE_ADDRESS_AWARE!).
Пометка вашей программы флагом IMAGE_FILE_LARGE_ADDRESS_AWARE указывает операционной системе: «давай, дай мне доступ к этой дополнительной памяти пользовательского адресного пространства», в результате адреса выше 2-х Гб становятся возможными возвращаемыми значениями в функциях выделения памяти. Если вы установите флаг «Top down» в предпочтениях менеджера памяти, вы можете указать менеджеру памяти выделять память сначала по старшим адресам, таким образом, вы заставите свою программу работать на высоких адресах сразу же, а не когда заполнится остальное место. Это очень удобный режим для проверки вашей программы в конфигурации /3GB, посольку он заставляет скорее, чем в обычном режиме, использовать проблемные адреса.
Итак, давайте включим IMAGE_FILE_LARGE_ADDRESS_AWARE для нашей программы:
project Project1;
uses
Windows; // для определения IMAGE_FILE_LARGE_ADDRESS_AWARE
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}
...
end.
Или (IMAGE_FILE_LARGE_ADDRESS_AWARE = $20 или 32):
…и посмотрим, как это изменит ситуацию:
Больше 2 Гб — что и требовалось показать (кстати, это же является примером и к предыдущему мифу).
Статус мифа: busted.
Миф №6: режим /3GB позволит мне выделить 1 гигантский блок памяти в 3 Гб
Просто то, что у вас есть аж 3 Гб виртуального адресного пространства, ещё не означает, что вы можете выделить один гигантский блок памяти размером 3 Гб. Мы уже видели (в мифе №3), что в виртуальное адресное пространство может быть фрагментировано, и вы не сможете выделить большой кусок за раз.
Стандартные дыры в виртуальном адресном пространстве не изменились: это 64 Кб внизу и 64 Кб около границы в 2 Гб.
Более того, системные DLL продолжают загружаться по их предпочтительным базовым адресам, которые лежат ниже границы 2 Гб. Куча процесса и другие типичные данные также откусывают понемногу от вашего виртуального адресного пространства.
В результате то, что пользовательское виртуальное адресное пространство практически равно 3 Гб, ещё не значит, что всё свободное пространство представлено одним блоком. Дыры около границы 2 Гб не дают вам получить непрерывного участка даже в 2 Гб.
Примечание: некоторые люди могут захотеть попробовать переместить системные DLL по другим адресам, чтобы освободить побольше места, но это не сработает по нескольким причинам. Во-первых, конечно же, этим вы не избавитесь от пробела в
64Кб около2Гб-ной границы. Во-вторых, система выделяет и другие данные, такие как блоки с информацией о потоках (thread information blocks) и переменные окружения, до того, как ваша программа получит шанс на выполнение; так что к тому времени, как ваша программа сможет выделять память, адресное пространство уже будет занято.Кроме того, системе действительно нужно, чтобы некоторые ключевые системные DLL были загружены по одним и тем же адресам во всех процессах. Например, ловушка
syscallдолжна находиться в фиксированном месте, чтобы обработчик ловушки режима ядра опознал её как допустимую ловушкуsyscall, а не как недопустимую инструкцию. Также этого требует отладчик, чтобы он мог использовать функциюCreateRemoteThreadдля внедрения точки останова в процесс.
Статус мифа: busted.
Миф №7: 32-х разрядная программа не может выделить более 3 Гб в своём адресном пространстве
Как мы увидели выше (в мифе №5), включение режима /3GB позволяет вам выделить память больше 2 Гб, но в том эксперименте вы могли столкнуться об ограничение в 3 Гб. Утверждается, что 32-х разрядная программа, скомпилированная с IMAGE_FILE_LARGE_ADDRESS_AWARE, не может выделить более 3 Гб памяти.
Кажется, что легенда подтверждена в мифе №5? Но не так быстро!
Создадим такую программу:
program Project1;
{$APPTYPE CONSOLE}
uses
Windows;
{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE}
const
ReserveSize = 1024; // 1024 * 64 Kb - резерв для работы по выводу результатов
IncSize: Cardinal = 64 * 1024; // выделения по 64 Kb
var
Sz: Cardinal;
LasrErr: Cardinal;
Reserve: Pointer;
begin
// Сохранили резерв
Reserve := VirtualAlloc(nil, ReserveSize * IncSize, MEM_RESERVE, PAGE_READWRITE);
// Цикл по определению максимума
Sz := ReserveSize * IncSize;
while Assigned(VirtualAlloc(nil, IncSize, MEM_RESERVE, PAGE_READWRITE)) do
Inc(Sz, IncSize);
LasrErr := GetLastError;
// Отпустили резерв, чтобы у нас была память для обработки и вывода результатов
VirtualFree(Reserve, 0, MEM_RELEASE);
// Смотрим, что получилось
if LasrErr = ERROR_NOT_ENOUGH_MEMORY then
WriteLn('ERROR_NOT_ENOUGH_MEMORY')
else
WriteLn(LasrErr);
WriteLn('Allocated: ', Sz, ' (', Sz div (1024 * 1024), ' MB)');
ReadLn;
end.
Эта программа пытается исчерпать память кусками по 64 Кб. Кроме того, она держит резерв памяти, чтобы выполнить WriteLn и работу со строками в конце (в самом деле, если вы исчерпаете всю память, то не сможете вывести результат). Программа также помечена флагом IMAGE_FILE_LARGE_ADDRESS_AWARE, что даёт ей доступ к памяти больше 2 Гб.
Теперь запустим эту 32-х разрядную программу на 64-х разрядной системе (никаких дополнительных действий вроде включения спец. режимов не требуется):
Это ж без малого аж 4 Гб для 32-х разрядной программы! Т.е. почти двукратное увеличение по сравнению с обычными 2 Гб. Круто.
Статус мифа: plausible.
Миф №8: 32-х разрядная операционная система не может использовать все 4 Гб оперативной памяти
Максимальный для 32-разрядных систем объем памяти – это 128 Гб, как указано в спецификации на Windows Server 2003 Datacenter Edition.
Такое ограничение связано с тем, что в более мощных системах структуры, применяемые диспетчером памяти для отслеживания физической памяти, потребляли бы слишком большую часть пространства виртуальных адресов. Диспетчер памяти отслеживает страницы памяти при помощи массива, называемого базой данных PFN, и в целях оптимизации производительности отображает все содержимое этой базы в виртуальную память. Так как каждая страница памяти представлена структурой данных объемом
28байт, в системе с физической памятью емкостью128Гб для размещения базы данных PFN потребуется930Мб. В32-разрядных ОС Windows предусмотрено пространство виртуальных адресов объемом4Гб, зависящее от оборудования и по умолчанию распределяемое между текущим процессом пользовательского режима (например, блокнотом) и системой. В таких условиях база данных PFN объемом980Мб занимает почти половину из доступных2Гб системной части пространства виртуальных адресов, а значит, на отображение ядра, драйверов устройств, системного кэша и других структур данных системы остается всего1Гб:По той же причине в таблице ограничений объема памяти указаны пониженные лимиты при загрузке в режиме
/3GB. Дело в том, что для этого режима характерна такая схема разделения физической памяти, при которой процессам пользовательского режима достается3Гб, а системе – всего1Гб. В целях повышения производительности в ОС Windows Server 2008 для системных нужд резервируется более значимая доля адресного пространства. Для этого максимальный объем физической памяти, поддерживаемый в32-разрядных версиях ОС, сокращается до64Гб.
Но разрушители легенд не были бы разрушителями легенд, если бы они верили на слово. Поэтому они должны это проверить.
Берём виртуальную машину, устанавливаем ей количество ОЗУ в 4 Гб и запускаем. Что же мы видим?
Что-то не очень похоже на обещанные 128 Гб. В чём же дело?
Дело в том, что ограничение в 128 Гб — это ограничение серверных ОС. Клиентские ОС (а Windows XP и Windows 7 — это клиентские ОС) имеют ограничения в 4 Гб.
Ну, это ничего не объясняет. Во-первых, почему такая разница? Это маркетинговый ход? Во-вторых: где же наши обещанные 4 Гб? Мы видим всего 3.5 Гб.
Во-первых, в ходе тестирования Windows выяснилось, что если разрешить использование памяти более 4 Гб, то многие системы аварийно завершают работу, зависают и отказываются загружаться. Происходит это из-за того, что некоторые драйверы устройств (в особенности аудио- и видеоустройств) запрограммированы на работу с физическими адресами в пределах 4 Гб. Эти драйверы, оказывается, обрубают адреса свыше 4 Гб, что приводит к повреждению содержимого памяти со всеми вытекающими последствиями. В серверных же системах, которые, как правило, оснащаются менее специфичными устройствами с относительно простыми и надежными драйверами, подобные проблемы обнаружены не были. Выявленные недостатки экосистемы драйверов заставили применительно к клиентским версиям ОС отказаться от работы с памятью в объеме свыше 4 Гб, несмотря на то, что теоретически её адресация возможна (обращаю внимание, что речь идёт о физической памяти, а не о виртуальном адресном пространстве, которое даже теоретически не может быть больше 4 Гб в 32-х разрядной системе).
Во-вторых, фактический лимит поддержки объема памяти ниже. Кроме того, он зависит от набора микросхем и характеристик подключенных устройств. Дело в том, что в таблицу физических адресов включается не только оперативная память, но и память устройств. При этом, для совместимости с 32-разрядными операционными системами, которые не способны обрабатывать адреса свыше 4 Гб, в системах x86 и x64 память устройств отображается ниже границы адресации 4 Гб. Предположим, что в системе установлено 4 Гб оперативной памяти, а окна в память сетевых адаптеров, аудио- и видеоустройств в сумме составляют 500 Мб, тогда 500 Мб из 4 Гб оперативной памяти окажутся за границей адресации — и мы получим доступные только 3.5 Гб физической памяти.
Даже если система оснащена всего 2 Гб физической памяти, может случиться так, что часть её окажется недоступной под управлением 32-разрядной версии Windows. Причиной тому – наборы микросхем, практикующие агрессивное резервирование областей памяти для устройств. Хотя такой сценарий, конечно, достаточно редок.
Статус мифа: plausible.
Миф №9: вам нужно включать режим /3GB, если у вас есть больше 2 Гб физической памяти
Физическая память — это не виртуальная память.
Я не уверен, какой логический процесс привёл к рождению этого мифа. Это не может быть из-за неверной интерпретации соответствия один-к-одному виртуальной и физической памяти, поскольку отображение явно не один-к-одному. Обычно у вас намного больше виртуальной памяти, чем физической. Свободная физическая память не имеет соответствия ни в одном виртуальном адресном пространстве. А разделяемая память обозначена в нескольких виртуальных адресных пространствах, хотя соответствует одним и тем же страницам физической памяти.
Этот миф разрушен в предыдущем расследовании, где никакого режима /3GB мы не включали, но получили 3.5 Гб памяти.
Статус мифа: busted.
Миф №10: большой .exe файл — это плохо, потому что он тратит память
Замечу, что, конечно же, в этом мифе имеется в виду физическая память, а не виртуальная — в том смысле, что большее потребление физической памяти — это нагрузка на систему, т.е. плохо. Очевидно, что виртуальное адресное пространство включает в себя .exe файл, так что тут даже нечего обсуждать.
Итак, я создал два идентичных проекта (пустых VCL приложения). Но во втором приложении я сделал Project / Resources and images и выбрал 110-мегабайтный файл:
Компиляция и мы получаем два файла — в примерно 5 и 110 Мб (включена отладочная информация TD32):
Запускаем обе программы и…
Неожиданно оказывается, что обе программы, несмотря на двадцатикратную разницу в размере, потребляют полностью идентичное количество оперативной памяти! Сюрприз.
Мне кажется, что этот миф получается из-за того, что люди не видят разницы между оперативной памятью и виртуальным адресным пространством.
Смысл в том, что при загрузке программы (или DLL) её файл проецируется на адресное пространство процесса с помощью механизма проецируемых в память файлов. Как и виртуальная память, проецируемые файлы позволяют резервировать регион адресного пространства и передавать ему физическую память. Различие между этими механизмами состоит в том, что в последнем случае физическая память не выделяется из страничного файла (файла подкачки), а берется из файла, уже находящегося на диске. Как только файл спроецирован в память, к нему можно обращаться так, будто он целиком в нее загружен.
Проецируемые файлы применяются для:
- загрузки и выполнения EXE- и DLL-файлов. Это позволяет существенно экономить как на размере страничного файла, так и на времени, необходимом для подготовки приложения к выполнению;
- доступа к файлу данных, размещенному на диске. Это позволяет обойтись без операций файлового ввода-вывода и буферизации его содержимого;
- разделения данных между несколькими процессами, выполняемыми на одной машине (в Windows есть и другие методы для совместного доступа разных процессов к одним данным — но все они так или иначе реализованы на основе проецируемых в память файлов);
Вот почему мы видим увеличение на +110 Мб виртуальной памяти у второго процесса — потому что туда спроецирован больший по размеру .exe файл.
При вызове из потока функции CreateProcess система действует так:
- Отыскивает ЕХЕ-файл, указанный при вызове функции
CreateProcess; - Создает новый объект ядра «процесс»;
- Создает адресное пространство нового процесса;
- Резервирует регион адресного пространства — такой, чтобы в него поместил ся данный ЕХЕ-файл. Желательное расположение этого региона указывается внутри самого ЕХЕ-файла. По умолчанию базовый адрес ЕХЕ-файла — $00400000.
- Отмечает, что физическая память, связанная с зарезервированным регионом, — ЕХЕ-файл на диске, а не страничный файл.
Спроецировав ЕХЕ-файл на адресное пространство процесса, система обращается к разделу ЕХЕ-файла со списком DLL, содержащих необходимые программе функции. После этого система, вызывая LoadLibrary, поочередно загружает указанные (а при необходимости и дополнительные) DLL-модули. Всякий раз, когда для загрузки DLL вызывается LoadLibrary, система выполняет действия, аналогичные описанным выше в пп. 4 и 5.
После увязки EXE- и DLL-файлов с адресным пространством процесса начинает исполняться стартовый код EXE-файла. Подкачку страниц, буферизацию и кэширование система берет на себя. Например, если код в ЕХЕ-файле переходит к команде, не загруженной в память, возникает ошибка. Обнаружив её, система перекачивает нужную страницу кода из образа файла на страницу оперативной памяти. Затем она отображает страницу оперативной памяти на должный участок адресного пространства процесса, тем самым позволяя потоку продолжить выполнение кода. Все эти операции скрыты от приложения и периодически повторяются при каждой попытке процесса обратиться к коду или данным, отсутствующим в оперативной памяти.
Иными словами, при загрузке процесса, не имеет значения, какого размера будет .exe файл — файл будет лишь спроецирован на адресное пространство, но в физической памяти будет только первая страница кода. Все остальные части будут загружены только по мере обращения кода к ним.
Но наша работа на этом ещё не закончена. Когда же размер файла имеет значение?
Ответ: при упаковке или шифровании.
В Windows загрузчик читает лишь заголовок и таблицу импорта файла, а затем проецирует его на адресное пространство процесса так, будто бы файл является частью виртуальной памяти, хранящейся на диске. Подкачка с диска происходит динамически — по мере обращения к соответствующим страницам памяти, причем загружаются только те из них, которые действительно нужны.
Например, если в текстовом редакторе есть модуль работы с таблицами, он не будет загружен с диска до тех пор, пока пользователь не захочет создать (или отобразить) свою таблицу. Причем неважно — находится ли этот модуль в динамической библиотеке или в основном файле. Загрузка таких «монстров», как Delphi и Word, как бы «размазывается» во времени и к работе с приложением можно приступать практически сразу же после его запуска. А что произойдет, если файл упаковать? Правильно — он будет должен считаться с диска целиком (!) и затем — опять-таки, целиком — распаковаться в оперативную память.
Стоп! Откуда у нас столько оперативной памяти? Ее явно не хватит и распакованные страницы придется вновь скидывать на диск! Как говорится: за что боролись, на то и напоролись. Причем, если при проецировании неупакованного EXE-файла оперативная память не выделяется, (во всяком случае, до тех пор, пока в ней не возникнет необходимость), то уж распаковщику без памяти никак не обойтись. А поскольку оперативной памяти никогда не бывает в избытке, она может быть выделена лишь за счет других приложений! Отметим также, что в силу конструктивных особенностей железа и архитектуры операционной системы, операция записи на диск заметно медленнее операции чтения.
Важно понять: Windows никогда не сбрасывает на диск не модифицированные страницы проецируемого файла. Зачем ей это? Ведь в любой момент их можно вновь считать из оригинального файла. Но при распаковке модифицируются все страницы файла! Значит, система будет вынуждена «гонять» их между диском и памятью, что существенно снизит общую производительность всех приложений в целом.
Еще большие накладные расходы влечет за собой сжатие динамических библиотек. Для экономии памяти страницы, занятые динамической библиотекой совместно используются всеми процессами, загрузившими эту DLL (об этом — в следующем мифе). Но как только один из процессов пытается что-то записать в память, занятую DLL, система мгновенно создает копию модифицируемой страницы и предоставляет ее в «монопольное» распоряжение процесса-писателя. Поскольку распаковка динамической библиотеки происходит в контексте процесса, загрузившего ее, система вынуждена многократно дублировать все страницы памяти, выделенные библиотеке, фактически предоставляя каждому процессору свой собственный экземпляр DLL. Предположим, одна DLL размером в мегабайт, была загружена десятью процессами — посчитайте: сколько памяти напрасно потеряется, если она сжата!
Таким образом, под Windows сжимать исполняемые файлы нецелесообразно — вы платите гораздо больше, чем выручаете.
Статус мифа: busted.
Миф №11: Delphi приложение занимает много памяти
Я уже высказывался на эту тему и даже говорил о типичных ошибках при поиске утечек памяти.
Самое время взяться за эту легенду!
Я взял последнюю версию Delphi на сегодня — Delphi XE (ведь известно, чем старше версия Delphi, тем больший размер она имеет) и создал в ней два пустых приложения — VCL Forms и консольное. Запускаем и видим — VCL Forms:
И консольное:
Вы только посмотрите на эти числа: около 80 и 57 мегабайт! И это — пустые приложения. Просто ужасно.
Кажется, что легенда подтверждена, но так ли это? Давайте посмотрим внимательнее.
Напомню, что это — виртуальная память. Никого не волнует, сколько её вы захапаете. Для экосистемы приложений важно, сколько оперативной (физической) памяти вы занимаете. Почему? Ну, чем плохо, что вы тратите много ресурсов? В абстрактном вакууме — ничем. Если в системе есть ресурсы — тратьте их как угодно. Но на практике машину не покупают для запуска исключительно вашей программы, поэтому здесь важно, что на машине работают и другие программы. Вот почему важно, чтобы вы оставляли как можно больше совместных ресурсов свободными — чтобы ими могло воспользоваться больше программ. Но ведь виртуальное адресное пространство не является общим ресурсом! Оно своё у каждой программы. А общий ресурс — это процессор и физическая память. Вот они делятся между всеми программами, в отличие от виртуального адресного пространства, которое выделяется каждой программе в эксклюзивное пользование.
Мне кажется, что этот миф происходит из неосознавания этой связи.
Но легенда ещё не разрушена — что там у нас с физической памятью?
Каждой программе при её работе выделяется физическая память. Программа не может работать с памятью, выгруженной на диск. Если программа выполняет код, находящийся в файле подкачки, либо обращается к данным, выгруженным на диск, то система автоматически загрузит эти код и данные в оперативную память. Если там есть свободное место. Если его там нет — то часть старых данных (к которым давно не было обращения или к ним обращаются редко) будет выгружена на диск, чтобы освободить место для новых данных. Оперативная память тратится и на такие вещи как дисковый кэш.
Итак, что там с оперативной памятью в нашей программе? Если вы посмотрите на снимки экрана выше, то получите два числа: 10'072 Кб для VCL Forms и 3'548 Кб для консольного (колонка «Working Set Size», это значение также называется «песочницей» программы и показывается Диспетчером Задач в колонке «Память»). Кажется, что это огромные значения — в несколько раз больше размера .exe файлов (который равен 894 Кб для VCL Forms и 22 Кб для консольного).
Кажется, что теперь легенда подтверждена? Но не будем спешить с выводами.
Как мы узнали выше, память в адресном пространстве, хотя и выделяется с кратностью в 64 Кб, но минимальным блоком (для прочих операций с памятью) является страница памяти (она имеет размер в 4 Кб на текущих редакциях Windows). Кроме того, мы узнали, что одна и та же страница памяти может присутствовать (быть ассоциирована) с несколькими программами (адресными пространствами). Вспомните проецируемые в память файлы.
К чему я это говорю?
Когда вы запускаете одну и ту же программу второй раз, система просто открывает другое проецируемое в память представление объекта «проекция файла», идентифицирующего образ исполняемого файла. С помощью проецируемых в память файлов несколько одновременно выполняемых экземпляров программы могут совместно использовать один и тот же код, загруженный в оперативную память. Т.е. система просто-напросто проецирует страницы виртуальной памяти, содержащие код и данные .exe файла, второй программы на адресное пространство первого экземпляра программы.
Если один экземпляр программы модифицирует какие-либо данные, размещенные на общей (разделяемой)странице данных, система перехватывает эту попытку, выделяет новый блок памяти, копирует в него нужную программе страницу и после этого разрешает запись в новый блок памяти. Благодаря этому механизму (называемому copy-on-write — копирование при записи), работа остальных экземпляров программы не нарушается. Аналогичная цепочка событий происходит и при отладке приложения. Например, запустив несколько экземпляров программы, вы хотите отладить только один из них. Вызвав отладчик, вы ставите в строке исходного кода точку прерывания. Отладчик модифицирует ваш код, заменяя одну из команд на языке ассемблера другой — заставляющей активизировать сам отладчик. И снова система использует копирование при записи. Обнаружив попытку отладчика изменить код, она выделяет новый блок памяти, копирует туда нужную страницу и позволяет отладчику модифицировать код на этой копии.
Иными словами, то, что вашей программе выделено 10'072 Кб оперативной памяти, — ещё не означает, что это «её вина». Т.е. эти 10'072 Кб — не лично ваша собственность, они совместно используются ещё и другими программами. Можно ли узнать, сколько в этих 10 Мб ваших данных? Да, можно. Это значения в колонке «WS Private» (private working set). Для VCL Forms мы получаем 1'604 Кб, а для консольного — 876 Кб. Это и есть те реальные значения, на которые ваша программа загружает систему. Ради сравнения — эти же программы на Delphi 3 дают 692 Кб и 332 Кб соответственно. Достаточно мало и намного меньше тех значений, о которых обычно думает тот, кто кричит: «ай как много занимает памяти Delphi приложение». И это в системе, где куча свободной ОЗУ и нет давления на память — т.е. это почти максимум. В условиях давления на память эти значения были бы ещё ниже. Посмотрите, как в мифе №1 размер потребляемой Total Commander-ом оперативной памяти снизился с 1'080 Кб до 136 Кб в условиях нехватки памяти (выделения 2x512 Мб на системе с 256 Мб ОЗУ). И заметьте, что даже при выделении 1 Гб памяти, песочница вашей программы осталась очень компактной — менее 2 Мб: потому что к этой памяти мы не обращались. Мы её только выделили.
Статус мифа: busted.
Миф №12: доступ к невыделенной памяти приводит к возбуждению Access Violation
Гм, разве каждый ребёнок не знает про то, что прежде чем использовать память, её надо выделить? Попытка доступа к невыделенной памяти неизменно закончится ошибкой доступа к памяти. Звучит разумно и миф кажется правдоподобным. Но давайте посмотрим, так ли это на самом деле:
program Project1;
{$APPTYPE CONSOLE}
uses
Windows;
var
P: Pointer;
begin
P := VirtualAlloc(nil, 1024, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
FillChar(P^, 2 * 1024, 0);
ReadLn;
end.
Чтобы исключить влияние менеджера памяти Delphi, мы выделяем память не через GetMem / AllocMem, а прося её напрямую у системы — через VirtualAlloc. Суть примера в том, что мы выделяем 1 Кб памяти (1024 байт), а потом записываем в них 2 Кб. Казалось бы, это должно привести к возбуждению Access Violation, но при запуске программы мы обнаруживаем, что она успешно выполняется до конца.
В чём же дело? Как мы помним, выделение памяти происходит с гранулярностью в 64 Кб, а размер выделяемых блоков кратен размеру страницы — т.е. 4 Кб. Да, это странное поведение (почему бы не выделять память с гранулярностью в 4 Кб?), но у него есть причины. Но это означает, что если вы просите у системы 1 Кб, то будет выделено все 4 Кб, а 60 Кб, следующие за этой страницей, останутся неиспользуемыми (ведь следующий блок памяти может начинаться лишь на границе +64 Кб от текущего).
Вот и причина для успешного выполнения этого кода — на самом деле код программы выделяет не 1 Кб, а 4 Кб. Это легко можно подтвердить, если заменить множитель 2 в FillChar на 5: 5 Кб больше 4 Кб, поэтому теперь программа вылетит.
Несмотря на это, миф нельзя назвать полностью разрушенным, ведь технически эта память выделена — даже хотя вы логически этого не просили. Поэтому я бы назвал его «правдоподобным».
Статус мифа: plausible.
Миф №13: освобождение памяти уменьшает показатели использования памяти программы
Многие ожидают, что освобождая память, вы возвращаете её системе. И снова это выглядит логично, но что будет на практике?
Создадим пустое приложение с двумя кнопками и Edit-ом: первая кнопка будет выделять память, указанную в Edit-е, а вторая — её освобождать:
procedure TForm1.Button1Click(Sender: TObject); begin Tag := Integer(AllocMem(StrToInt(Edit1.Text) * 1024)); end; procedure TForm21.Button2Click(Sender: TObject); begin FreeMem(Pointer(Tag)); end;
Запустим программу и попробуем щёлкать на кнопках со значением 10240 (10 Мб).
Ну, при выделении памяти потребление виртуальной памяти приложением подскакивает на +10 Мб, а при освобождении — уменьшается.
Миф подтверждён? Мы так легко не сдаёмся: попробуйте повторить этот же эксперимент, указывая значения вроде 1 или 4. Теперь вы можете заметить, что при освобождении памяти, занятая виртуальная память не изменяется. Более того, если вам достаточно повезёт, то вы увидите, что при выделении памяти, потребление виртуальной памяти не увеличивается!
(примечание: это плавающее поведение; возможно, вам придётся поэкспериментировать с выделением/освобождением памяти, прежде чем вы его воспроизведёте)
Неужели мы открыли неизвестный науке принцип возникновения памяти из ничего? Вовсе нет — вспомните, что работа с памятью в Delphi (да и других языках тоже) идёт через менеджер памяти — это прослойка между вами и системой, которая, грубо говоря, упаковывает ваши запросы на память в один пакет. В предыдущем мифе мы уже увидели, что при выделении всего 1 Кб памяти на деле расходуется в несколько раз больше памяти — из-за её гранулярности. Чтобы память не пропадала зря, менеджер памяти располагает в одном блоке памяти сразу несколько ваших запросов на память — вот почему потребление памяти может не изменяться при выделении/освобождении памяти: потому что память будет «выделена» в уже существующем блоке памяти, либо же при освобождении памяти менеджер памяти не сможет освободить блок памяти, потому что там есть и другие занятые регионы (либо он может просто придержать свободный блок, на случай, если вы сейчас захотите заново выделить память).
Заметьте, что это не является какой-то «плохой» вещью, как вам может показаться. Мы уже разрушили такой миф: вспомните, что потребление оперативной памяти программы крайне слабо связано с выделением памяти в ней. Вы можете выделить 1 Гб памяти, но в оперативной памяти система даст вам всего 2 Мб. Так и с этой, временно не используемой памятью: она никак не мешается и лежит в файле подкачки, пока вам не понадобится или пока её не освободят.
Конечно же, сказанное не означает утечку памяти — вся эта память будет в итоге возвращена системе. Либо с течением времени, либо при последующих освобождениях памяти. Так что в целом, на длительном участке, миф выполняется: освобождение памяти в вашем коде уменьшает потребление памяти программой, но это может быть не так локально, на достаточно малых участках кода.
Статус мифа: plausible.
Примечание: предыдущие два мифа приводят к неизбежному заключению: вы не можете судить о том, валиден (т.е. допустим, корректен) ли данный вам указатель (т.е. была ли под него выделена память) или нет. Более того, это нельзя сделать даже при отсутствии факторов, про которые говорится в мифах:
P := AllocMem(1024); FreeMem(P); N := AllocMem(1024);
Валиден ли P? Логически — нет, т.к. у нас есть явное освобождение памяти. Но любая проверка покажет вам, что он допустим: потому что память под N выделится ровно на том же самом месте, где были данные от P. Таким образом, P и N будут указывать на одно и то же место в памяти, даже хотя P более не считается допустимым по этому коду. Поскольку вы не можете гарантировать неосуществимость этой операции на практике (за исключением специальных отладочных сборок, конечно же), то вы и не можете судить о допустимости указателя, основываясь на любых его проверках: какую-бы проверку вы ни предложили бы, всегда найдётся ситуация, когда проверка будет давать ложно-положительный результат.
Поэтому, когда вы освобождаете память, всегда присваивайте указателю nil: тогда его проверка на допустимость будет тривиальной if Assigned(P) then.
Миф №14: Obj.Free не приводит к Obj = nil
Если вы работали с объектами, то знаете, что одним из способов удалить объект — это вызывать метод Free, который проверит ссылку объекта и вызовет его деструктор. Правда ли, что после этого ссылка объекта не изменяется и продолжает указывать на бывший объект?
Это очень легко проверить:
procedure TForm1.Button1Click(Sender: TObject);
var
O: TObject;
begin
O := TObject.Create;
O.Free;
if Assigned(O) then
ShowMessage('O <> nil');
end;
Запустите её — и вы получите сообщение.
Почему так происходит? Ну, об этом можно догадаться. Free — это метод объекта. Да, в него передаётся указатель на объект, как и в любой другой метод (далее, в методе, этот указатель становится Self) — но передаётся по значению. Иными словами, Self := nil внутри Free не изменит O — ведь любые изменения в параметре, переданном по значению, не влияют на исходное значение параметра. Free не может изменить O даже теоретически.
Если бы это было не так, то вызов конструктора для создания объекта мог бы быть таким:
O.Create;
Если бы изменения в Self влияли бы на исходное значение, то подобный вызов мог бы создать объект и записать ссылку в O. Но вместо этого мы пишем:
O := TObject.Create;
Что означает создание объекта и запись ссылки в переменную.
Так же и с освобождением объекта: если вы хотите об-nil-ить ссылку — передавайте её по ссылке (в FreeAndNil):
FreeAndNil(O);
FreeAndNil освободит объект и присвоит O в nil. Я уже упоминал, что FreeAndNil является самым правильным вариантом освобождения объекта из трёх (вызов деструктора Destroy, вызов прослойки Free и вызов FreeAndNil).
Статус мифа: confirmed.
Миф №15: если программа не освободит память, то в системе останется мусор и она замедлится
За эти 14 мифов мы уже столько раз запускали тестовые программы на выделение огромных количеств памяти (некоторые — даже больше размера установленной памяти в системе), но после закрытия программ система продолжала работать как ни в чём не бывало, что этот миф разрушен ещё до того, как я его озвучил.
Но особо дотошные могут его проверить самостоятельно: это будет домашнее задание для начинающих разрушителей легенд. Как бы вы его проверили?
Суть в том, что при закрытии программы, все ресурсы, которые были с ней ассоциированы (виртуальная память, физическая память, открытые файлы и т.п.), автоматически удаляются/закрываются/очищаются системой. Конечно, в системе есть глобальные ресурсы/объекты, не являющиеся частью состояния программы (и которые, таким образом, не меняются при закрытии программы), но виртуальная память к ним не относится.
Мне кажется, что миф пришёл из времён, когда адресное пространство было тождественно физической памяти: его ошибочно перенесли на современные системы, не разбираясь, как они работают.
Статус мифа: (totally) busted.
P.S. Я не уверен, насколько удачным получился этот пост, ведь он написан достаточно нестандартно. Возможно, стоило добавить парочку взрывов. Шучу.
P.P.S. Интересно, что много мифов говорят про различные ограничения 32-х разрядных процессов и не существуют для 64-х разрядных приложений с их фантастическими 16-ю эксабайтами адресного пространства.
См. также:
- Серия про ключ
/3GBот Реймонда Чена - Серия «Преодолевая ограничения Windows» от Марка Руссиновича
Читать далее: Адресное пространство под микроскопом.
Время на прочтение3 мин
Количество просмотров323K
По запросу в любой поисковой системе «файл подкачки windows» можно получить тысячу-другую скопированных друг у друга, либо немного отличающихся ответов по выбору оптимальных размеров для pagefile.sys.
Самые распространенные советы выглядят примерно следующим образом: для машин с маленьким ОЗУ нужно задавать размер файла подкачки k*RAM, где RAM — объем физической памяти, k — какой-нибудь коэффициент, коих много самых разнообразных. И 1,5, и 2, и даже 3 встречал. Если же планок памяти стоит на 4Гб и больше, то «смело отключайте виртуальную память в принципе».
Статья о том, стоит ли верить ли этим советам, и если да, то насколько.
Что такое файл подкачки?
pagefile.sys, он же файл подкачки — файл, представляющий собой виртуальную память, которая позволяет одновременно выполняться большому количеству процессов, которые все сразу не смогли бы поместиться в физической памяти.
По умолчанию после установки Windows файл подкачки увеличивается автоматически при заполнении текущего объема.
Если отключить файл подкачки
Если попытаться отключить файл подкачки в windows 7, система выдаст предупреждающее окно, в котором сообщит о неприятных последствиях:
Отсюда следует, что не стоит полностью отказываться от использования виртуальной памяти, иначе в случае краха не получится даже проанализировать причину сбоя. Указанный на скриншоте минимальный размер в 1МБ берется из расчета конфигурации дампа памяти в настройках «загрузка и восстановление»:
Если выбрать для записи отладочной информации полный дамп, то размер увеличивается на несколько порядков. У меня он составил 400МБ.
Кроме отсутствия возможности записи дампа, после отключения файла подкачки может появится назойливое сообщение о нехватке памяти. Появление его будет сопровождаться жуткими тормозами ресурсоемких приложений.
Если перенести файл подкачки на другой раздел
Куча статей по оптимизации вашей ОС рекомендует перенести файл подкачки на отдельно созданный и отформатированный в FAT32 раздел жесткого диска. При этом повышается быстродействие и уменьшается фрагментация этого файла.
При подобных манипуляциях не стоит забывать, что файл подкачки должен присутствовать в системном разделе для корректной записи отладочной информации. Выбирать приходится между быстродействием и возможностью сбора данных о возникших неприятностях.
Размер файла подкачки
Вернемся
к нашим апельсинам
к вопросу об оптимальном размере. Перекопав множество статей, информационных изданий и даже рекомендации Microsoft, я так и не нашел четкого и однозначного ответа на этот вопрос. Да и не нашел бы, как стало мне ясно после прочтения перевода статьи Марка Руссиновича Преодолевая ограничения Windows: виртуальная память. В заключении приведу ссылки на перевод и оригинал, а сейчас постараюсь объяснить, откуда же взять размер файла.
Для начала потребуется утилита Process Explorer, она является бесплатным аналогом дефолтного Task Manager’a, но обладает многими преимуществами. Скачать можно по ссылке.
После запуска Process Explorer’a выберите самые ресурсоемкие в плане используемой памяти приложения, которые используете в повседневной жизни, и запустите их все одновременно. В окне Process Explorer’a нажмите CTRL+I или выберите в меню View/System Information, из всего многообразия представленных в окне данных нужно выбрать область Commit Charge
Значение Peak — пиковое значение выделенной памяти для всех приложений, складываемое из физической и виртуальной памяти.
Далее вооружаемся калькулятором и вычитаем из этого значения размер оперативной памяти. Если получается отрицательное значение — берем требуемые системой 400МБ (может быть другое значение), необходимые для создания дампа. Если получается положительное значение — выставляем таким минимальное и максимальное значение файла подкачки. Можно подстраховаться и установить «про запас» максимум выше, но тогда вырастет фрагментация файла в случае увеличения его размеров. Поэтому лучше зафиксировать на одном месте.
Литература
Статья Марка Руссиновича Pushing the Limits of Windows: Virtual Memory;
Перевод на русский язык Преодолевая ограничения Windows: виртуальная память;
Описание программы Process Explorer .
Содержание
- Преодолевая ограничения Windows: физическая память
- Управление ОЗУ, виртуальной памятью, подкачками страниц и памятью в Windows
- Аннотация
- Процессы и адресные пространства
- Файл подкачки
- Производительность, ограничения архитектуры и ОЗУ
- Мониторинг использования ОЗУ и виртуальной памяти
- Преодолевая ограничения windows виртуальная память
Преодолевая ограничения Windows: физическая память
Размещено 28/11/2008
Эта публикация будет первой в серии под названием «Преодолевая ограничения Windows», которая ожидает вас в ближайшие месяцы. В ней разъясняется механизм потребления ОС Windows и приложениями тех или иных ресурсов, описываются ограничения этих ресурсов, обусловленные условиями лицензирования или особенностями реализации, предлагаются методики получения статистики потребления ресурсов и диагностики утечек. Для умелого управления системами Windows нужно понимать, как ОС координирует физические (в том числе ресурсы процессора и памяти) и логические (например, виртуальную память, дескрипторы и объекты диспетчера окон) ресурсы. Представление об ограничениях таких ресурсов и умение отслеживать их потребление позволяет составить картину основных потребителей, скорректировать характеристики системы в расчете на определенную нагрузку и выявить приложения, провоцирующие утечки ресурсов.
Помимо воздействия на производительность, объем физической памяти обуславливает ограничения других ресурсов. К примеру, объем невыгружаемого пула (буферов операционной системы, выделяемых из физической памяти) напрямую зависит от объема физической памяти. Кроме того, характеристики физической памяти определяют предел виртуальной памяти системы, объем которой примерно равен сумме емкости физической памяти и максимального объема всех настроенных файлов подкачки. Наконец, объем физической памяти косвенно сказывается на максимальном количестве процессов. Об этом я подробно расскажу в предстоящей публикации об ограничениях, связанных с процессами и потоками.
Ограничения памяти в ОС Windows Server
Особенности поддержки физической памяти в ОС Windows продиктованы ограничениями оборудования, условиями лицензирования, характеристиками структур данных операционной системы и вопросами совместимости драйверов. На странице «Ограничения памяти в различных выпусках ОС Windows» (на английском языке) веб-узла MSDN изложены ограничения памяти, характерные для различных версий Windows и номеров SKU в рамках каждой версии.
Диспетчер памяти мог бы высвободить память путем выборочного отображения фрагментов базы данных PFN в системные адреса по мере необходимости, однако этот вариант слишком сложен и имеет потенциал снижения производительности, связанного с дополнительными операциями отображения и отмены отображения. Лишь недавно стали появляться системы настолько производительные, чтобы такой вариант можно было рассматривать как реалистичный. Впрочем, так как размер системной части адресного пространства не является ограничением для отображения всей базы данных PFN в 64-разрядных версиях ОС Windows, поддержка большего объема памяти реализована именно в них.
К моменту разработки пакета обновления 2 (SP2) для ОС Windows XP появление клиентских систем с объемом памяти свыше 4 ГБ уже прогнозировалось, что заставило разработчиков приступить к интенсивному тестированию Windows XP на подобных системах. Кроме того, в пакете обновления 2 (SP2) для ОС Windows XP была реализована поддержка расширений физических адресов (PAE) по умолчанию для устройств, поддерживающих технологию No Execute, что, во-первых, необходимо для предотвращения исполнения данных (DEP), а во-вторых, обеспечивает возможность поддержки памяти в объеме более 4 ГБ.
В ходе тестирования выяснилось, что многие системы аварийно завершают работу, зависают и отказываются загружаться. Происходит это из-за того, что некоторые драйверы устройств, в особенности аудио- и видеоустройств, которые, в основном, встречаются в клиентских системах, а не в серверах, запрограммированы на работу с физическими адресами в пределах 4 ГБ. Эти драйверы, оказываются, обрубают адреса свыше 4 ГБ, что приводит к повреждению содержимого памяти со всеми вытекающими последствиями. В серверных же системах, которые, как правило, оснащаются менее специфичными устройствами с относительно простыми и надежными драйверами, подобные проблемы обнаружены не были. Выявленные недостатки экосистемы драйверов заставили применительно к клиентским версиям ОС отказаться от работы с памятью в объеме свыше 4 ГБ, несмотря на то, что теоретически её адресация возможна.
Следовательно, оснащение 32-разрядной системы с клиентской версией ОС Windows памятью объемом 3 ГБ и выше, вполне возможно, не принесет желаемого эффекта. В ОС Windows 2000, Windows XP и Windows Vista RTM с объемом доступной памяти можно ознакомиться в диалоговом окне System Properties (Свойства системы), на странице Performance (Быстродействие) диспетчера задач. В ОС Windows XP и Windows Vista (в том числе с пакетом обновления 1 (SP1)) эти сведения можно также получить с помощью служебных программ Msinfo32 и Winver. С выходом пакета обновления 1 (SP1) для ОС Windows Vista некоторые из этих инструментов стали указывать объем установленной, а не доступной, памяти, о чем сказано в специальной статье базы знаний Майкрософт.
Как свидетельствует утилита Msinfo32, при загрузке моего ноутбука под управлением 32-разрядной версии Vista доступно 3,5 ГБ памяти из четырех установленных.
Обратите внимание на два разрыва между страницами: от 9F0000 до 100000 и от DFE6D000 до FFFFFFFF (4 ГБ). Характерно, что при загрузке того же компьютера с 64-разрядной версией Vista все 4 ГБ памяти отмечаются как доступные, а 500 МБ ОЗУ, оставшиеся выше отметки 4 ГБ, задействуются следующим образом.
Так почему же эти разрывы возникают ниже границы 4 ГБ? Ответить на этот вопрос нам поможет диспетчер устройств. Запустите оснастку «devmgmt.msc», выберите в меню View (Вид) команду Resources by Connection (Ресурсы по подключению) и раскройте узел Memory (Память). На моем ноутбуке основным потребителем отображаемой памяти устройств, что не удивительно, является видеоадаптер, на долю которого приходится 256 МБ в диапазоне E0000000-EFFFFFFF:
Оставшийся объем распределяется между другими устройствами. Кроме того, согласно консервативной оценке микропрограммы в период загрузки, шина PCI резервирует для устройств дополнительные диапазоны.
Потребление адресов памяти ниже 4 ГБ особенно заметно в мощных игровых системах с высокопроизводительными видеоадаптерами. Я как-то купил в магазине, специализирующемся на игровых компьютерах, систему с 4 ГБ ОЗУ и двумя видеоадаптерами по 1 ГБ каждый. Я не стал специально указывать свои предпочтения по части ОС, предполагая, что установлена 64-разрядная версия Vista. Вопреки моим ожиданиям, выяснилось, что они установили 32-разрядную версию, а значит, операционной системе доступно лишь 2,2 ГБ из установленной памяти. После установки 64-разрядной версии, по данным программы Meminfo, в диапазоне памяти между 8FEF0000 и FFFFFFFF образовался громадный разрыв.
Диапазон физических адресов от 7E700000 до FFFFFFFF зарезервирован шиной PCI и устройствами. Таким образом, даже теоретически пространство физических адресов не может превышать 7E700000 байт (1,976 ГБ), но поскольку часть этого диапазона резервируется для памяти устройств, Windows сообщает о том, что общий объем памяти составляет 1,97 ГБ.
Так как теперь для получения сертификата о подписывании драйверов изготовители оборудования должны предоставлять в лаборатории WHQL драйверы как для 32-разрядных, так и для 64-разрядных версий Windows, большинство современных драйверов вполне справляются с физическим адресами свыше 4 ГБ. 32-разрядные версии Windows в силу наличия риска, который очень трудно оценить, будут по-прежнему игнорировать память выше границы 4 ГБ. При этом изготовители оборудования в тех случаях, когда этому ничто не препятствуют, переходят (или, по крайней мере, должны переходить) на 64-разрядные версии.
Вывод прост: потребление памяти в полном объеме, каким бы существенным он ни был (конечно, в рамках ограничений версии), возможно только в 64-разрядных версиях Windows. При покупке специализированного игрового компьютера обязательно попросите изготовителя оборудования сразу установить 64-разрядную версию операционной системы.
Дефицит доступной памяти свидетельствует о том, что к физической памяти активно обращаются процессы или система. Если же объем доступной памяти оказывается близким к нулю в течение длительного времени, значит, имеет смысл установить дополнительную память. Существует несколько способов мониторинга доступной памяти. В ОС Windows Vista опосредованно отслеживать объем доступной памяти помогает индикатор Physical Memory Usage History (Хронология использования физической памяти) диспетчера задач. Его значение должно быть постоянно приближено к 100%. Вот как выглядит диспетчер задач в моей настольной системе с объемом памяти 8 ГБ (кстати, не много ли у меня памяти?!).
Во всех версиях ОС Windows график доступной памяти можно вывести при помощи системного монитора. Для этого нужно активировать счетчик Available Bytes (Доступно байт) из группы счетчиков Memory performance (Производительность памяти).
С текущей величиной доступной памяти можно ознакомиться в диалоговом окне System Information (System Information) программы Process Explorer или (в предшествующих Vista версиях ОС Windows) на странице Performance (Быстродействие) диспетчера задач.
Источник
Управление ОЗУ, виртуальной памятью, подкачками страниц и памятью в Windows
Исходная версия продукта: Windows 7 Пакет обновления 1, Windows Server 2012 R2
Исходный номер КБ: 2160852
Аннотация
В этой статье содержатся основные сведения о реализации виртуальной памяти в 32-битных версиях Windows.
В современных операционных системах, таких как Windows, приложения и многие системные процессы всегда ссылались на память с помощью адресов виртуальной памяти. Виртуальные адреса памяти автоматически преобразуются оборудованием в реальные (ОЗУ) адреса. Только основные части ядра операционной системы обходят этот перевод адресов и напрямую используют реальные адреса памяти.
Виртуальная память всегда используется, даже если объем памяти, требуемой для всех запущенных процессов, не превышает объем оперативной памяти, установленной в системе.
Процессы и адресные пространства
Всем процессам (например, исполняемым приложениям), работающим под 32-битными версиями Windows, назначены адреса виртуальной памяти (виртуальное адресное пространство), в диапазоне от 0 до 4 294 967 295 (2*32-1 = 4 ГБ), независимо от того, какой объем ОЗУ установлен на компьютере.
В конфигурации Windows по умолчанию 2 гигабайта (ГБ) этого виртуального адресного пространства предназначены для частного использования каждого процесса, а остальные 2 ГБ совместно используются всеми процессами и операционной системой. Как правило, приложения (например, Блокнот, Word, Excel и Acrobat Reader) используют только часть 2 ГБ частного адресного пространства. Операционная система назначает кадры страниц ОЗУ только используемым страницам виртуальной памяти.
Расширение физического адреса (PAE) — это функция 32-битной архитектуры Intel, которая расширяет адрес физической памяти (ОЗУ) до 36 битов. PAE не меняет размер виртуального адресного пространства (который остается на уровне 4 ГБ), а только объем фактической оперативной памяти, который может быть решен процессором.
Перевод между 32-битным адресом виртуальной памяти, используемым кодом, который работает в процессе, и 36-битным ОЗУ-адресом автоматически и прозрачно обрабатывается оборудованием компьютера в соответствии с таблицами перевода, которые поддерживаются операционной системой. Любая виртуальная страница памяти (32-битный адрес) может быть связана с любой физической страницей ОЗУ (36-битным адресом).
В следующем списке описывается, сколько ОЗУ поддерживается различными версиями и выпусками Windows (в мае 2010 г.):
| Версия Windows | ОЗУ |
|---|---|
| Windows NT 4.0 | 4 ГБ |
| Windows 2000 Professional | 4 ГБ |
| Windows 2000 Standard Server | 4 ГБ |
| Windows 2000 Advanced Server | 8 ГБ |
| Windows 2000 Datacenter Server | 32 ГБ |
| Windows XP Professional | 4 ГБ |
| Windows Server 2003 Web Edition | 2 ГБ |
| Windows Server 2003 Standard Edition | 4 ГБ |
| Windows Server 2003 Enterprise Edition | 32 ГБ |
| Windows Server 2003 Datacenter Edition | 64 ГБ |
| Windows Vista | 4 ГБ |
| Windows Server 2008 Standard | 4 ГБ |
| Windows Server 2008 Enterprise | 64 ГБ |
| Windows Server 2008 Datacenter | 64 ГБ |
| Windows 7 | 4 ГБ |
Файл подкачки
ОЗУ — это ограниченный ресурс, тогда как в большинстве практических целей виртуальная память не ограничена. Может быть много процессов, каждый из которых имеет собственное 2 ГБ частного виртуального адресного пространства. Если память, используемая всеми существующими процессами, превышает объем доступной оперативной памяти, операционная система перемещает страницы (4 КБ) одного или более виртуальных адресных пространств на жесткий диск компьютера. Это освободит кадр ОЗУ для других видов использования. В системах Windows эти страницы с страницами хранятся в одном или Pagefile.sys файлах в корне раздела. В каждом разделе диска может быть один такой файл. Расположение и размер файла страницы настраиваются в свойствах системы (нажмите кнопку «Дополнительные»,«Производительность» и нажмите кнопку «Параметры»).
Пользователи часто задают вопрос о том, насколько большим должен быть этот pagefile? На этот вопрос не существует одного ответа, так как он зависит от объема установленной оперативной памяти и объема виртуальной памяти, требуемой для рабочей нагрузки. Если других сведений нет, то в качестве отправной точки является обычная рекомендация в 1,5 раза больше установленной оперативной памяти. В серверных системах обычно необходимо иметь достаточный объем оперативной памяти, чтобы не было нехватки и не использовался pagefile. В этих системах не может быть полезного предназначения для обслуживания большого pagefile. С другой стороны, если место на диске достаточно, то сохранение большого размера (например, в 1,5 раза больше установленного ОЗУ) не вызывает проблем, а также избавляет от необходимости беспокоиться о том, насколько большим будет размер ОЗУ.
Производительность, ограничения архитектуры и ОЗУ
В любой компьютерной системе по мере увеличения нагрузки (количества пользователей, объема работы) производительность снижается, но нелинейно. Любое увеличение нагрузки или спроса после определенного момента приводит к существенному снижению производительности. Это означает, что некоторым ресурсам не хватает ресурсов и они стали узким местом.
В определенный момент не удается увеличить ресурс, который находится в нехватке. Это означает, что достигнут предел архитектуры. Ниже lyly reported architectural limits in Windows include the following:
Это относится в частности к Windows Server 2003, но также может применяться к Windows XP и Windows 2000. Однако в Windows Vista, Windows Server 2008 и Windows 7 не все эти архитектурные ограничения имеются. Ограничения на объем памяти пользователя и ядра (здесь цифры 1 и 2) одинаковы, но ресурсы ядра, такие как PTES и различные пулы памяти, являются динамическими. Эта новая функция позволяет использовать как страницную, так и невыгежную память. Это также позволяет PTES и пулу сеансов увеличиваться за пределы, которые были рассмотрены ранее, до того момента, когда все ядро исчерпано.
Часто найденные и кавычках:
На сервере терминалов до использования 4 ГБ ОЗУ будет использоваться 2 ГБ общего адресного пространства.
В некоторых случаях это может быть верно. Однако необходимо отслеживать систему, чтобы узнать, применимы ли они к конкретной системе. В некоторых случаях эти заявления являются выводами определенных Windows NT 4.0 или Windows 2000 и не обязательно применимы к Windows Server 2003. В Windows Server 2003 были внесены значительные изменения, чтобы снизить вероятность того, что эти архитектурные ограничения будут фактически достигнуто на практике. Например, некоторые процессы, которые находились в ядре, были перемещены в процессы без ядра, чтобы уменьшить объем памяти, используемый в общем виртуальном адресной области.
Мониторинг использования ОЗУ и виртуальной памяти
Системный монитор — это инструмент, который позволяет отслеживать производительность системы и определять местоположение узкого места. Чтобы запустить монитор производительности, нажмите кнопку «Начните», выберите «Панель управления»,«Администрирование» и дважды щелкните «Монитор производительности». Вот сводка по некоторым важным счетчикам и их сведениям:
Память, зафиксированные вбайтах: этот счетчик является показателем потребности в виртуальной памяти.
Здесь показано, сколько бытов было выделено процессами и на что операционная система зафиксирует кадр страницы ОЗУ или слот страницы в этом окле (или, возможно, и то, и другое). По мере того как размер фиксных данных превышает объем доступной оперативной памяти, увеличивается размер подкачка, а используемый размер подкачка также увеличивается. В определенный момент действия по разгонам начинают значительно влиять на производительность.
Процесс, рабочий набор, _Total: этот счетчик является показателем активного использования виртуальной памяти.
Этот счетчик показывает, сколько ОЗУ требуется для того, чтобы виртуальная память, используемая для всех процессов, была в оперативной памяти. Это значение всегда является кратным 4096, что является размером страницы, используемой в Windows. Так как потребность в виртуальной памяти выходит за пределы доступной оперативной памяти, операционная система настраивает объем виртуальной памяти процесса в рабочем наборе, чтобы оптимизировать доступное использование ОЗУ и свести к минимуму разгибание по сети.
Файл подкачка, используемый %pagefile: этот счетчик является показателем того, какая часть файла страниц фактически используется.
Этот счетчик используется для определения размера подкачка. Если этот счетчик достигает 100, то подкачка заполнена, и все будет работать. В зависимости от настояния рабочей нагрузки, возможно, необходимо, чтобы размер подкачка был достаточно большим, чтобы он использовался не более 50–075 процентов. Если используется большая часть подкачка, наличие более одного на разных физических дисках может повысить производительность.
Память, страницы/с: этот счетчик является одной из наиболее распространенных мер.
Высокое значение этого счетчика не обязательно означает, что узкое место производительности связано с нехваткой ОЗУ. Операционная система использует систему подкачки для других целей, кроме замены страниц из-за чрезмерного обязательства памяти.
Память, выходные данные страниц/с: этот счетчик показывает, сколько страниц виртуальной памяти было записано на страницу, чтобы освободить кадры страниц ОЗУ для других целей каждую секунду.
Это лучший счетчик для отслеживания, если вы подозреваете, что разгон является узким местом производительности. Даже если количество зафиксированных данных больше установленного ОЗУ, если в большинстве периодов времени значение выходных данных страниц в секунду низкое или нулевое, проблем с производительностью недостаточного объема ОЗУ не возникает.
Память, кэш-память, невыгкупаемая память пула, память, количество на странице пула, память, общее количество системных кодов, память, общее количествобайтов системного драйвера:
Сумма этих счетчиков является показателем того, сколько из 2 ГБ общей части виртуального адресного пространства размером 4 ГБ фактически используется. Используйте их, чтобы определить, достигает ли ваша система одного из ограничений архитектуры, которые были рассмотрены ранее.
Память, доступное МБайт: этот счетчик измеряет объем ОЗУ, доступный для удовлетворения требований к виртуальной памяти (либо для новых выделений, либо для восстановления страницы из подкачка).
Если ОЗУ не хватает (например, «Зафиксированные», больше установленного ОЗУ), операционная система попытается сохранить определенную часть установленной оперативной памяти доступной для немедленного использования путем копирования страниц виртуальной памяти, которые не используются в активном режиме, на этот подкачка. Таким образом, этот счетчик не достигает нуля и не обязательно является хорошим показателем того, не хватает ли системе ОЗУ.
Источник
Преодолевая ограничения windows виртуальная память
Предисловие
Теория:
Memory type Limit in 32-bit Windows Limit in 64-bit Windows Общее виртуальное адресное пространство 4 ГБ 16 ТБ Виртуальное адресное пространство для одного 32-разрядного процесса 2 ГБ
до 3 ГБ, если приложение компилируется с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE и система загружается с ключом /3GB 2 ГБ
4 ГБ, если приложение компилируется с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE
Виртуальное адресное пространство для одного 64-разрядного процесса — 2 ГБ
x64: 8 ТБ, если приложение компилируется с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE
Intel IPF: 7 ТБ, если приложение компилируется с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE
Следующие термины, в данном обзоре, равносильны:
1. Виртуальное адресное пространство процесса;
2. Виртуальная память процесса;
3. Виртуальная память.
Примечание: Ограничение VAS для режима ядра до 1 ГБ оказывает влияние на работу всей операционной системы, а не только на приложения, которым нужен большой объём VAS. Ключ /3GB влияет на все компоненты ядра, включая все драйверы. Включение /3GB может вызвать такие отрицательные эффекты, как снижение производительности и отказы распределения памяти с остановкой системы.
Чтобы приложение смогло использовать виртуальное адресное пространство больше 2ГБ оно должно быть скомпилировано с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE.
В 64-bit Windows для 32-bit процесса доступно:
2ГБ и 4ГБ (если приложение компилируется с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE).
Практика:
1. Почему не хватает памяти?!
Есть приложения у которых нет проблем из-за нехватки VAS: Adobe Photoshop, GIMP и т.п:
1. Память «не выделяется большими кусками».
2. Если «виртуальной памяти» недостаточно есть temp-файл, т.е. нет зависимости от размера VAS.
Из п.1 теории видно, что памяти даётся 32-bit приложению много 2ГБ (по-умолчанию), но в п.2 и п.3 описываются «подводные камни», на которых спотыкаются ряд приложений, далее пойдёт речь о таких приложениях и каким образом можно обходить эти «подводные камни».
Если приложение выделяет память «небольшими кусочками», то оно займёт всю доступную виртуальную память и уже после выдаст сообщение о нехватке памяти или вывалиться с ошибкой.
Если приложение выделяет память большими блоками, то оно практически сразу выдаст сообщение:
Связано это с тем, что первые 2ГБ виртуального адресного пространства процесса фрагментированы, разбиты на непрерывные области, и при попытке зарезервировать область памяти больше имеющейся приложение говорит, что недостаточно памяти.
Выберем Type Free (свободные области памяти), отсортируем столбец Size по убыванию:
В Options отметим Show Free Regions (отображать свободные области памяти), выберем Type Total (все области памяти), отсортируем столбец Address по возрастанию, в итоге видим, как поделено адресное пространство виртуальной памяти процесса:
Ниже на картинке показано, как это выглядит «на плоскости»:
2. Флаг IMAGE_FILE_LARGE_ADDRESS_AWARE
Если программист забыл или не помнил про флаг IMAGE_FILE_LARGE_ADDRESS_AWARE ничего это дело поправимое. Информация находится в заголовке исполняемого файла (PE).
Для начала надо определится стоит флаг или нет. Для это можно воспользоваться приложениями выводящими информация о PE (PE Viewer) или использовать редактор PE (PE editor), или т.п.
Для просмотра можно воспользоваться следующими приложениями:
Выставляем флаг: EDITBIN.EXE /LARGEADDRESSAWARE program.exe
Убираем флаг: EDITBIN.EXE /LARGEADDRESSAWARE:NO program.exe
Только выставить флаг можно с помощью 4GB Patch:
Для просмотра и изменения можно воспользоваться CFF Explorer, также позволяет менять и другие параметры:
Посмотрим, что будет иметь 32-bit приложение (7zG.exe) с выставленным флагом /LARGEADDRESSAWARE и чего не будет, если флаг не выставлен.
32-bit Windows
32-bit Windows
/3GB /USERVA=3072
7zG.exe флаг /LARGEADDRESSAWARE выставлен
64-bit Windows
64-bit Windows
7zG.exe флаг /LARGEADDRESSAWARE выставлен
3. Решение проблемы «нехватки памяти».
2. Использовать Win64 или Win32 + /3GB, у приложения должен быть выставлен флаг IMAGE_FILE_LARGE_ADDRESS_AWARE.
3. «Расчистить» адресное пространство вручную:
3.1. Убрать приложения (выгрузить\удалить), которые «мешают»;
3.2. Изменить базовые адреса dll-ок вручную ( для опытных пользователей).
3.1. Изменение базового адреса dll \ exe:
Примечание: Разработчику (программисту) обычно виднее с какого адреса должна грузиться его dll-ка и т.п.
необходимые условия для изменения базового адреса:
При одинаковых базовых адресах у dll-ок, или если адресное пространство занято, куда dll-ка хотела загрузиться, происходит операция переадресации, dll-ка загружается по другому свободному адресу. Старайтесь указывать адреса, которые свободны, чтобы переадресации не было.
Для изменения базового адреса можно воспользоваться утилитами ReBase.Exe, EDITBIN.EXE, и т.п.:
REBASE.EXE также может пакетно обрабатывать файлы, в результате dll-ки будут располагаться последовательно у первой будет указанный адрес, у остальных автоматом присвоятся новые большие адреса:
EDITBIN.EXE, может обрабатывать только по одному файлу:
EDITBIN.EXE /REBASE:BASE=0x10000000 z.dll
ReBase.Exe, EDITBIN.EXE имеют «защиту от дурака», если нет relocation table или адрес не кратен 64kB, то базовый адрес не будет изменён.
Примечание: замечено relocation section может не быть, а базовый адрес будет изменён (xpsp2res.dll), или relocation section присутствует, а базовый адрес не меняется. почему сие так непонятно.
Последовательность действий при изменении Image Base:
1. сделать резервную копию изменяемых dll \ exe;
2. скопировать в отдельную папку dll \ exe, для изменения адреса;
3. изменить базовый адрес;
4. заменить dll \ exe на изменённые.
Проблемы которые могут быть:
В данном случае проблема одна (три разновидности).
С одной стороны user32.dll перемещаться может, с другой стороны система этого не любит:
Пример:
32-bit Windows
Если пойдём влоб, то получил сообщение об ошибке:
будет достаточно если объединим три блока:
250048кБ (244МБ)
1230396кБ (1201МБ)
413984кБ (404МБ)
нам это должно дать непрерывный блок памяти примерно в 1850МБ
но т.к. dll-ки могут быть перемещены, то возьмём Process Explorer, офф.сайт.
Process Explorer сможет нам показать какие dll-ки подцепились к процессу, их базовые адреса, с каких адресов загрузились, и путь до этих dll:
FileBXH.dll и 7z.dll имеют одинаковый базовый адрес, соответственно FileBXH.dll была перемещена.
Изменим адреса начиная с 0x75000000 (мне так захотелось):
Примечание: Не забываем, что расположение dll подгоняется под адресное пространство конкретного процесса (7zG.exe), если dll-ки используются другими приложениями, могут возникнуть проблемы.
В итоге получим:
0x75000000 7z.dll
0x750E0000 FileBXH.dll
0x75100000 uxtheme.dll
3.2. Минимизация потенциальных проблем:
Примечание: Данные способы не относится к dll-кам, которые внедряются сторонним ПО.
1. Чтобы минимизировать потенциальные проблемы с изменёнными dll-ками их можно положить рядом с исполнимым файлом (*.exe).
Примечание: Способ работает в зависимости от приложения \ dll-ок.
2. Перенаправление DLL (DLL redirection)
DLL-ки будут загружаться сначала из папки приложения, если их там нет, то будут искаться в других папках.
Рекомендации, подробнее написано в DLL/COM Redirection on Windows
Если исполнимый файл Program.exe, то создать рядом файл (или папку) с именем Program.exe.local:
Положить рядом с исполнимым файлом Program.exe файл Program.exe.manifest,
например такого содержимого:
Пример:
В случае с 7zG.exe достаточно просто положить рядом uxtheme.dll:
DLL-ки, которые внедряются сторонним ПО
Осталось решить вопрос с FileBXH.dll
Оставим решение на «откуп системы», изменим базовый адрес на адрес, который всегда занят, чтобы было всегда перемещение (переадресация) (так делать не рекомендуется).
В конце пользовательского адресного пространства есть закрытый регион памяти в 64кБ.
Для общего случая можно взять адрес 0xFFFF0000:
3.3. «Будьте бдительными»
Существует достаточно большое количество утилит\PE-редакторов и т.п., с помощью которых можно изменять Image Base.
Но не всегда данная манипуляция проходит «корректно».
Воспользуемся для изменения базового адреса CFF Explorer — Rebuilder:
Изменим Image Base для следующих dll:
0x75000000 7z.dll
0x750E0000 FileBXH.dll
0x75100000 uxtheme.dll (замена dll-ки будет в системном каталоге)
после перезагрузки Windows получим:
проблема из-за uxtheme.dll
Источник
