Limits of Virtual Memory in Windows - Ограничения виртуальной памяти в Windows
Автор: egor23, 29.05.2008 14:34, последнее обновление 13.12.2009 21:55
обсуждение обзора на форуме


Предисловие
Теория
   1. Ограничения Windows
   2. "Барьеры" в адресном пространстве
   3. Недостаточно памяти
Практика
   1. Почему не хватает памяти?!
   2. Флаг IMAGE_FILE_LARGE_ADDRESS_AWARE
   3. Решение проблемы "нехватки памяти"
      3.1. Изменение базового адреса dll \ exe
      3.2. Минимизация потенциальных проблем
      3.3. "Будьте бдительными"
Литература


Предисловие

Данный обзор рассказывает об ограничениях виртуального адресного пространства (Virtual Address Space - VAS) процесса в 32-bit Windows, и в 64-bit Windows для 32-bit приложений. А также раскрывает причины появлений сообщений - "Недостаточно памяти". Если раньше при появлении неинформативного сообщения - "Недостаточно памяти", это списывалось на то, что установлено мало оперативной (физической) памяти, то теперь списывать на это не получается (установлено часто 2ГБ и более оперативной памяти), и возникает вопрос: Почему не хватает памяти?

Более подробно о VAS можно ознакомиться в следующих обзорах:
Обзор Virtual Address Space - VAS
Обзор механизмов виртуальной памяти Windows (продолжение "Обзор Virtual Address Space")

В блоге Марка Руссиновича есть статьи:
Преодолевая ограничения Windows: физическая память
Преодолевая ограничения Windows: виртуальная память

В данном обзоре не рассматриваются ограничения Windows по физической памяти, о них можно прочитать в следующих статьях:
Четыре гигабайта памяти - недостижимая цель?
Поддержка памяти большого размера в Windows Server 2003 и Windows 2000
Physical Address Extension (PAE) [1, c.458]
Operating Systems and PAE Support


Теория:

  1. Ограничения Windows.
    Полный список ограничений по памяти можно посмотреть на страничке Memory Limits for Windows Releases.
    Нас же интересуют только ограничения виртуального адресного пространства:

    Memory and Address Space Limits
    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

    Виртуальная память - В Windows реализована система виртуальной памяти, основанная на плоском (линейном) адресном пространстве. Она создаёт каждому процессу иллюзию того, что у него есть собственное большое и закрытое адресное пространство [1, c.15].

    Следующие термины, в данном обзоре, равносильны:
    1. Виртуальное адресное пространство процесса;
    2. Виртуальная память процесса;
    3. Виртуальная память.

    В 32-bit Windows:
    Размер виртуального адресного пространства - 4ГБ, распределено:
    2ГБ - user mode (пользовательский режим), 2ГБ - kernel mode (режим ядра).
    Т.е. приложению (процессу) доступно примерно 2ГБ виртуального адресного пространства в независимости от количества установленной физической памяти. Размер виртуального адресного пространства в режиме пользователя можно увеличить до 3ГБ используя специальные параметры (ключи) загрузки, 4-gigabyte tuning (4GT):

    Windows Vista \ Windows 2008 \ Windows 7
    BCDEdit /set increaseuserva xxxx (xxxx в МБ в диапазоне 2048 - 3072), BCDEdit /set.

    Windows XP \ Windows 2003
    /3GB /userva=xxxx (xxxx в МБ в диапазоне 2048 - 3072) в файле Boot.ini (Параметры, используемые в файле Boot.ini), рекомендуемый максимум значений userva 2900–3030, более детально можно узнать в статье.

    Примечание: Ограничение VAS для режима ядра до 1 ГБ оказывает влияние на работу всей операционной системы, а не только на приложения, которым нужен большой объём VAS. Ключ /3GB влияет на все компоненты ядра, включая все драйверы. Включение /3GB может вызвать такие отрицательные эффекты, как снижение производительности и отказы распределения памяти с остановкой системы.

    Чтобы приложение смогло использовать виртуальное адресное пространство больше 2ГБ оно должно быть скомпилировано с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE.

    Виртуальное адресное пространство
    (по-умолчанию)
    Виртуальное адресное пространство
    с параметром загрузки /3GB
    /userva=3072

    Старшие 2ГБ (0x80000000 - 0xFFFFFFFF)
    Младшие 2ГБ (0x00000000 - 0x7FFFFFFF)

    Старшие 1ГБ (0xC0000000 - 0xFFFFFFFF)
    Младшие 3ГБ (0x00000000 - 0xBFFFFFFF)

    Виртуальное адресное пространство (по-умолчанию)
    Виртуальное адресное пространство /3GB /userva=3072

    В 64-bit Windows для 32-bit процесса доступно:
    2ГБ и 4ГБ (если приложение компилируется с параметром IMAGE_FILE_LARGE_ADDRESS_AWARE).

  2. "Барьеры" в адресном пространстве.
    У каждого приложения, dll-ки и т.п., есть стартовый адрес (Image Base Address), с которого она должна начать загрузку. При загрузке приложения в его адресное пространство подгружаются разные dll-ки, которые использует приложение, а также dll-ки от других программ (Firewall, Antivirus, FileBox eXtender, Prio, ABBYY Lingvo, и т.п.). В результате в адресном пространстве появляются "барьеры".
    Также "барьеры" появляются в результате работы приложения, представляют собой "занятые" блоки адресного пространства.

  3. Недостаточно памяти.
    Большинство приложений резервируют адресное пространство всегда непрерывными блоками, и если им это не удаётся, то приложения или выдают сообщение: Недостаточно памяти; или "вываливаются" с ошибкой: Ошибка произошла т.к. недостаточно памяти; или "вываливаются" с ошибкой, которая мало о чём говорит; или виснут.

Практика:

В примерах использовались ОС Windows:
32-bit Windows - Windows XP Professional SP2
64-bit Windows - Windows XP Professional x64 Edition SP2


1. Почему не хватает памяти?!

Есть приложения у которых нет проблем из-за нехватки VAS: Adobe Photoshop, GIMP и т.п:
1. Память "не выделяется большими кусками".
2. Если "виртуальной памяти" недостаточно есть temp-файл, т.е. нет зависимости от размера VAS.

Из п.1 теории видно, что памяти даётся 32-bit приложению много 2ГБ (по-умолчанию), но в п.2 и п.3 описываются "подводные камни", на которых спотыкаются ряд приложений, далее пойдёт речь о таких приложениях и каким образом можно обходить эти "подводные камни".

Если приложение выделяет память "небольшими кусочками", то оно займёт всю доступную виртуальную память и уже после выдаст сообщение о нехватке памяти или вывалиться с ошибкой.

Если приложение выделяет память большими блоками, то оно практически сразу выдаст сообщение:

недостаточно памяти для выполнения операции...

7-zip, Squeez, WinRK и т.п. - при создании\распаковке архива;
Paint.NET, Xnview и т.п. - при создании\открытии изображения;
и т.п. список приложений в которых возникают такие проблемы большой.

Связано это с тем, что первые 2ГБ виртуального адресного пространства процесса фрагментированы, разбиты на непрерывные области, и при попытке зарезервировать область памяти больше имеющейся приложение говорит, что недостаточно памяти.

Чтобы увидеть, как фрагментировано адресное пространство конкретного процесса, в данный момент времени, воспользуемся утилитой VMMap, офф.сайт. Для примера возьмём приложение 7-zip 32bit, процесс 7zG.exe (7-Zip GUI) - вызывается из контекстного меню - 7-zip - Добавить к архиву...

Выберем Type Free (свободные области памяти), отсортируем столбец Size по убыванию:

VMMap -  Free - 7zG.exe

Максимальная свободная область виртуальной памяти - 1201.6МБ (1230488кБ).
В столбце Largest показаны области самого большого размера в каждом типе памяти.

В Options отметим Show Free Regions (отображать свободные области памяти), выберем Type Total (все области памяти), отсортируем столбец Address по возрастанию, в итоге видим, как поделено адресное пространство виртуальной памяти процесса:

VMMap - Total - 7zG.exe

Ниже на картинке показано, как это выглядит "на плоскости":

7zG.exe virtual memory fragmentation


2. Флаг IMAGE_FILE_LARGE_ADDRESS_AWARE

Если программист забыл или не помнил про флаг IMAGE_FILE_LARGE_ADDRESS_AWARE ничего это дело поправимое. Информация находится в заголовке исполняемого файла (PE).

Portable Executable (PE) - Портируемый формат исполняемых файлов.
Portable Executable - Wikipedia, the free encyclopedia
Portable Executable — Википедия
Спецификация PE - Microsoft Portable Executable and Common Object File Format Specification

Для начала надо определится стоит флаг или нет. Для это можно воспользоваться приложениями выводящими информация о PE (PE Viewer) или использовать редактор PE (PE editor), или т.п.

Для просмотра можно воспользоваться следующими приложениями:

ExeInfo

ExeInfo


Program to analyze PE files

Program to analyze PE files


Если флаг не выставлен, то его можно выставить хоть вручную - в нужном месте, нужный байтик поправить.

Для изменения флага (выставить\убрать) есть утилита EDITBIN.EXE идёт с Microsoft Visual Studio:
Microsoft Visual Studio
Microsoft Visual C++ 2005 Express Edition
Microsoft Visual Studio 2008 Express Edition
Windows SDK for Windows Server 2008 and .NET Framework 3.5
EDITBIN.EXE есть также в MASM32

Выставляем флаг: EDITBIN.EXE /LARGEADDRESSAWARE program.exe
Убираем флаг:       EDITBIN.EXE /LARGEADDRESSAWARE:NO program.exe


Только выставить флаг можно с помощью 4GB Patch:

4GB Patch


Для просмотра и изменения можно воспользоваться CFF Explorer, также позволяет менять и другие параметры:

CFF Explorer


Посмотрим, что будет иметь 32-bit приложение (7zG.exe) с выставленным флагом /LARGEADDRESSAWARE и чего не будет, если флаг не выставлен.

32-bit Windows

VMMap -  Free - 7zG.exe VMMap - Total - 7zG.exe


32-bit Windows
/3GB /USERVA=3072
7zG.exe флаг /LARGEADDRESSAWARE выставлен

VMMap -  Free - 7zG.exe VMMap - Total - 7zG.exe


64-bit Windows

VMMap -  Free - 7zG.exe VMMap - Total - 7zG.exe


64-bit Windows
7zG.exe флаг /LARGEADDRESSAWARE выставлен

VMMap -  Free - 7zG.exe VMMap - Total - 7zG.exe


В итоге:
при использовании параметра IMAGE_FILE_LARGE_ADDRESS_AWARE 32-bit приложение получит дополнительно непрерывный регион памяти:
в 32bit Windows - до 1023МБ;
в 64bit Windows - 2047МБ,
причём данные блоки будут доступны 32-bit приложению всегда.


3. Решение проблемы "нехватки памяти".

1. Связаться с автором приложения, объяснить суть проблемы, возможно исправит, но могут быть варианты:
1.1. Автор учтёт особенности VAS и внесёт исправления - приложение не будет зависеть от непрерывных блоков;
1.2. Автор учтёт особенности VAS, но уйдёт от решения проблемы, путём ограничения функционала приложения;
1.3. Автор учтёт особенности VAS, но ничего сделано не будет.

2. Использовать Win64 или Win32 + /3GB, у приложения должен быть выставлен флаг IMAGE_FILE_LARGE_ADDRESS_AWARE.

3. "Расчистить" адресное пространство вручную:
3.1. Убрать приложения (выгрузить\удалить), которые "мешают";
3.2. Изменить базовые адреса dll-ок вручную (для опытных пользователей).


3.1. Изменение базового адреса dll \ exe:

1. Данным способом нужно пользоваться обдуманно, может приводить к нестабильной работе ПО.
2. Данное решение - "на данный момент", т.к. в дальнейшем dll-ки могут обновиться, могут подцепиться другие dll-ки и т.п.
   (обновили ПО, поставили обновления на систему и т.п.)

Примечание: Разработчику (программисту) обычно виднее с какого адреса должна грузиться его dll-ка и т.п.

необходимые условия для изменения базового адреса:

1. У dll \ exe должна быть таблица переадресации (relocation table \ relocation section)
2. Базовый адрес должен быть кратен 64кБ (адрес записанный в HEX будет заканчиваться на четыре нуля, например - 0x12340000)
3. Базовй адрес может быть "любым" в пределах 0x00000000-0xFFFF0000

При одинаковых базовых адресах у dll-ок, или если адресное пространство занято, куда dll-ка хотела загрузиться, происходит операция переадресации, dll-ка загружается по другому свободному адресу. Старайтесь указывать адреса, которые свободны, чтобы переадресации не было.

Для изменения базового адреса можно воспользоваться утилитами ReBase.Exe, EDITBIN.EXE, и т.п.:

ReBase.Exe идёт с Microsoft Visual Studio
Microsoft Visual Studio
Windows SDK for Windows Server 2008 and .NET Framework 3.5

REBASE.EXE -b 0x10000000 z.dll

REBASE.EXE также может пакетно обрабатывать файлы, в результате dll-ки будут располагаться последовательно у первой будет указанный адрес, у остальных автоматом присвоятся новые большие адреса:

REBASE.EXE -b 0x10000000 *.dll
или
REBASE.EXE -b 0x10000000 @list_dll.txt
list_dll.txt - файл-список

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 перемещаться может, с другой стороны система этого не любит:

Error Explorer.exe
Error Explorer.exe
Error Explorer.exe



Пример:

32-bit Windows

Для примера возьмём приложение 7-zip 32bit , процесс 7zG.exe (7-Zip GUI) - вызывается из контекстного меню - 7-zip - Добавить к архиву...

Сделаем крайность - размер модели PPMd 1800МБ - нам нужен будет непрерывный блок памяти примерно в 1800МБ.

Если пойдём влоб, то получил сообщение об ошибке:

Error 7-zip


VMMap - Total - 7zG.exe VMMap - Total - 7zG.exe


будет достаточно если объединим три блока:
250048кБ   (244МБ)
1230396кБ (1201МБ)
413984кБ   (404МБ)
нам это должно дать непрерывный блок памяти примерно в 1850МБ

По VMMap нам нужно изменить адреса двух dll-ок:
7-zip.dll      - dll-ка от приложения 7-zip
uxtheme.dll - системная dll-ка

но т.к. dll-ки могут быть перемещены, то возьмём Process Explorer, офф.сайт.
Process Explorer сможет нам показать какие dll-ки подцепились к процессу, их базовые адреса, с каких адресов загрузились, и путь до этих dll:

Process Explorer 7zG.exe


Имеем:
FileBXH.dll   - dll-ка от стороннего приложения FileBox eXtender
7z.dll         - dll-ка от приложения 7-zip
uxtheme.dll - системная dll-ка

FileBXH.dll и 7z.dll имеют одинаковый базовый адрес, соответственно FileBXH.dll была перемещена.


Изменим адреса начиная с 0x75000000 (мне так захотелось):

Примечание: Не забываем, что расположение dll подгоняется под адресное пространство конкретного процесса (7zG.exe), если dll-ки используются другими приложениями, могут возникнуть проблемы.

REBASE.EXE -b 0x75000000 @list_dll.txt

В итоге получим:
0x75000000 7z.dll
0x750E0000 FileBXH.dll
0x75100000 uxtheme.dll

VMMap - Total - 7zG.exe Process Explorer 7zG.exe

Недостаток такого способа решения - dll-ки FileBXH.dll и uxtheme.dll используют практически все приложения с GUI.


3.2. Минимизация потенциальных проблем:

Примечание: Данные способы не относится к dll-кам, которые внедряются сторонним ПО.

1. Чтобы минимизировать потенциальные проблемы с изменёнными dll-ками их можно положить рядом с исполнимым файлом (*.exe).

Примечание: Способ работает в зависимости от приложения \ dll-ок.

2. Перенаправление DLL (DLL redirection)
DLL-ки будут загружаться сначала из папки приложения, если их там нет, то будут искаться в других папках.
Рекомендации, подробнее написано в DLL/COM Redirection on Windows

Если исполнимый файл Program.exe, то создать рядом файл (или папку) с именем Program.exe.local:

файл  Program.exe.local - dll-ки располагаются рядом с Program.exe
папка Program.exe.local - dll-ки располагаются или рядом с Program.exe, или в папке Program.exe.local, или и рядом и в папке.

Примечание: Данный способ не будет работать если есть manifest (встроен в exe, или лежит рядом - Program.exe.manifest).

3. Использовать файл manifest
Isolated Applications and Side-by-side Assemblies
Manifest Files Reference
Построение изолированных приложений и параллельных сборок C/C++

Положить рядом с исполнимым файлом Program.exe файл Program.exe.manifest,
например такого содержимого:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<file name="dll1.dll"/>
<file name="dll2.dll"/>
<file name="dll3.dll"/>
</assembly>



Пример:

В случае с 7zG.exe достаточно просто положить рядом uxtheme.dll:

VMMap - Total - 7zG.exe Process Explorer 7zG.exe


DLL-ки, которые внедряются сторонним ПО

Осталось решить вопрос с FileBXH.dll
Оставим решение на "откуп системы", изменим базовый адрес на адрес, который всегда занят, чтобы было всегда перемещение (переадресация) (так делать не рекомендуется).
В конце пользовательского адресного пространства есть закрытый регион памяти в 64кБ.
Для общего случая можно взять адрес 0xFFFF0000:

REBASE.EXE -b 0xFFFF0000 FileBXH.dll


VMMap - Total - 7zG.exe Process Explorer 7zG.exe



3.3. "Будьте бдительными"

Существует достаточно большое количество утилит\PE-редакторов и т.п., с помощью которых можно изменять Image Base.
Но не всегда данная манипуляция проходит "корректно".
Воспользуемся для изменения базового адреса CFF Explorer - Rebuilder:

CFF Explorer

Изменим Image Base для следующих dll:
0x75000000 7z.dll
0x750E0000 FileBXH.dll
0x75100000 uxtheme.dll (замена dll-ки будет в системном каталоге)

после перезагрузки Windows получим:

Error Explorer.exe

проблема из-за uxtheme.dll

Остались без рабочего стола - неприятно, но не смертельно, зато у 7-zip всё замечательно :)

VMMap - Total - 7zG.exe Process Explorer 7zG.exe




Литература:
  1. М. Руссинович, Д. Соломон. Внутреннее устройство Microsoft Windows: Windows Server 2003, Windows XP и Windows 2000., — 4-е изд. СПб.: Питер, 2005.



Specially for forum.ru-board.com