Mikle писал(а):Понятно. Просто я просмотрел много разных справок, и везде с уверенностью пишут про два гига, иногда только уточняя, "если память вообще есть".
В данном случае нужно читать не справку, а что-то, что даёт полноценное понимание концепции работы памяти/виртуальной памяти в Windows.
Процессу действительно дано два гига виртуального адресного пространства. Два гига виртуального адресного пространства — не то же самое, что два гига памяти.
Представим, что наступил коммунизм/социализм/утопические времена. Каждому, кто хочет ездить на своём личном автомобиле этот автомобиль выдают по первому требованию. Каждому выдаваемому автомобилю выдаётся госномер с каким-то числом и буквенным идентификатором города. И всё-таки возможна ситуация, когда автомобиль выдать не могут: возможно что, исчерпан физический запас автомобилей и транспортного средства, которое можно было бы выдать, просто нет — не произвели его, а возможно, что машин, вышедших с конвейера и ждущих выдачи полно, но исчерпан пул номерных знаков — то дело не в том, что таблички с номерным знаком нет, а в том, что номера 5-значные и в данном городе уже выдано 100000 автомобилей. Так, что машину хоть и можно было бы выдать человеку, но невозможно присвоить ей не занятый уже кем-то другим номерной идентификатор. Причём запас автомобилей общий на всю страну, а вот диапазон номерных знаков на каждый город свой собственный.
Так что есть в целом 3 причины отказа в выдаче машины человеку:
1) Кончились машины в стране, хоть свободные номера в городе ещё есть
2) Исчерпан диапазон номерных знаков вашего города, хотя сами машины в запасе имеются
3) Нет ни машин, ни свободных номеров
Аналогичная ситуация с памятью:
Есть адресное пространство, которое является аналогией банка номерных знаков. Причём подобно тому, как в каждом городе может быть своя машина с номером 123456, у каждого процесса своё адресное пространство, поэтому один и тот же адрес в двух разных процессах не должен содержать одно и то же.
А есть набор private-страниц памяти, которые могут быть выданы в пользование процессу, что аналогично набору всех автомобилей на площадке автозавода. Подобно площадке автозавода, который один на всю страну (хотя их может быть и несколько, но машины на каждой из таких площадок не привязаны к какому-то одному городу и могут быть отданы в любой город, откуда бы ни поступил запрос), набор private-страниц глобален на всю систему и лимитирован размером файла подкачки (хотя их тоже может быть несколько в системе, если так указать в настройках).
Кроме private-страниц, которые выделяются процессу чистыми, есть ещё mapped-страниц: страницы, появляющиеся на свет путём проецирования произвольных файлов в адресное пространство процесса, но количество mapped-страниц не лимитировано размерами файла подкачки, по крайней мере до тех пор, пока mapped-страница не превратится в private-страницу, а это происходит, если попытаться что-то записать в такую страницу и при этом она имеет copy-on-write атрибут.
Страницы в адресном пространстве можно:
1) Зарезервировать (Reserve). Это аналогично тому, что вы у местных властей просите выделить вам госномер без предоставления машины. Это чистая формальность и бюрократия: номер просто числится за вами, но никакой физической машины вам при этом не выдаётся. В отличие от госномеров, страницы в адресном пространстве резервируются блоками, размер которых кратен 64К (для x86-архитектур). Смысл резервирования чисто бюрократический: можно зарезервировать сразу огромный блок и гарантировать, что если вдруг понадобится, он не будет фрагментирован. Разумеется, это работает если внутри процесса все придерживаются правила «никогда не выделяем страницы из регионов, которые не резервировали мы сами» — со стороны системы нет механизмов, препятствующих этому. Поскольку резервирование это чистой воды формальность и бюрократия, ничто не мешает написать программу, резервирующую все доступные свободные региона в АП процесса и запустить 200 таких процессов. Резервирование — не более чем создание информационной записи, что в таком-то процессе такой-то диапазон адресов уже зарезервирован, и исключительно для того, чтобы при повторной попытке зарезервировать что-то в том же месте ответить отказом. Объём файла подкачки и объём физической памяти никак не лимитирует объём, который можно зарезервировать ни в пределах одного процесса, ни в пределах системы (*).
2) Выделить (Commit). Выделить страницу можно только в уже зарезервированном регионе. Нельзя сделать так, чтобы страница была выделена, но регион, на который она приходится, не был зарезервирован. Для упрощения, VirtualAlloc при попытке выделить страницы без предварительного резервирования, сначала резервирует необходимый регион, а потом в нём выделяет запрошенное число страниц.
Выделение аналогично требованию предоставить вам реальный автомобиль. Предполагается, что либо вы перед этим зарезервировали за собой определённый госномер, либо же автоматически с выделением реального автомобиля вам будет присвоен госномер. Невозможна ситуация, когда вам дали физический автомобиль, а госномера у вас нет.
Объём всех выделенных страниц для всех процессов вместе взятых ограничен размером файла подкачки (или файлов подкачки в сумме, если их несколько). Примерно как объём всех выданных в пользование автомобилей не может быть выше, чем количество автомобилей, произведённых на автозаводе. Возможна ситуация, когда вы запрашиваете выдачу автомобиля, а вам отказывают, потому что в госфонде автомобилей нет. Но кто-то в вашем или даже другом городе может отказаться от автомобиля, и он вернётся в госфонд, и тогда при повторном запросе его выдадут именно вам.
3) Освободить, то есть отдать обратно в пользование системе (decommit). Это аналогично тому, что вы отказываетесь о автомобиля и он переходит в государственный фонд. При этом номер, который был за вами закреплён, за вами остаётся. А вот автомобиль, который вернулся в госфонд, может быть впоследствии выдан кому-то (в том числе и вам)
в вашем же или совершенно другом городе.
4) Разрезервировать (release). Это аналогично тому, что вы отказываетесь от госномера/диапазона номеров и они могут быть присвоены кому-то другому (в том числе и вам) в будущем
в вашем же городе.
В свете вышесказанного, вот это заявление:
Mikle писал(а):Интересно, есть хоть один компьютер в мире, на котором я действительно мог бы получить под 2 Гб одним куском? Что это за фантастическая конфигурация должна быть?
выглядит как «Есть ли во вселенной хоть одна страна, в которой я мог бы житя в одном из городов значиться владельцем 100000 автомобилей, при том, что номера во всех городах пятизначные». Ответ: «Нет, потому что для этого надо попасть в город, где абсолютно ни за кем не закреплено ни одной машины, а это нереально, потому что у других людей уже есть машины, а даже если бы у простых людей машин не было совсем, всё равно какое-то количество машины закреплено за гос. аппаратом города, так что для использования частными лицами в лучшем случае будет доступно хоть и 99%, но не все 100% этого диапазона. Но это не делает слова о том, что в каждом городе
теоретически может значится 100000 автомобилей (а практически — это ограничено количеством автомобилей, существующих в стране)».
Так вот, 2 Гб — это объём адресного пространства, доступного из пользовательского режима. Часть этого пространства в любом случае будет недоступна. Первые 64 Кб недоступны априори, остальная часть будет фрагментирована стеками потоков, TEB/TIB-блоками потоков, PEB-блоком, загруженными DLL-шками, а также просто регионами памяти (в том числе и просто зарезервированными), которые были заняты по мере загрузки хотя бы даже системных DLL.
От физической конфигурации ПК это вообще не зависит. Хоть 64 мб физпамяти у него, хоть 500 Гб.
Mikle писал(а):Я не ожидал, что IDE так неэкономно расходует память, непонятно, зачем её так фрагментировать?
Экономно/неэкономно — это понятие, которое можно применять к множеству закоммиченных страниц. К множеству зарезервированных страниц более применимо понятие оптимально/неоптимально. Ведь можно 1000 крошечных блоков разместить впритык друг к другу, и можно равномерно размазать по всему адресному пространству, и тогда максимальный размер участка, который можно зарезервировать, станет никак не больше 1/1000 полного размера адресного пространства.
Что касается «зачем её так фрагментировать»: практически нигде (за исключением одного места) IDE, а точнее Ruby+EB не выделяет память с помощью VirtualAlloc, указывая при этом адрес начала блока, который будет зарезервирован/выделен. Только VirtualAlloc даёт такую возможность. Но если передавать в VirtualAlloc ноль, то система решает сама, а более высокоуровневые функции выделения памяти вообще не предоставляют такой возможности — система сама решает, где именно будет выделен блок памяти. Поэтому со стороны IDE не никакого злого умысла или халатного отношения к памяти. То, что память становится такой фрагментированной — это выбор и решение ОС, а не самой IDE. DLL, например, загружаются по базовому адресу, который вшит в саму DLL, если только он не оказывается занятым чем-то другим.
Mikle писал(а):Ведь IDE отлично загружается и работает на XP с 512 Мб (даже и меньше) ОЗУ всего на всю систему.
Объём физической памяти вообще ничего значит в плане лимитирования того, сколько памяти может быть выделено (*). Играет роль объём файла подкачки. Но и, несмотря на эту поправку, какая связь между объёмом выделяемой памятью и степенью фрагментации адресного пространства?
В массив из миллиона элементов можно записать тысячу значений. Можно в начало или конец, тогда останется свободный участок из 999000 элементов. Можно записать в середину, тогда останется свободный участок в начале из 499500 элементов и свободный участок в конце из 499500 элементов. А можно равномерно размазать по всему массиву: тогда, куда ни ткни, самый длинный свободный участок будет иметь длину 999 элементов, и найти непрерывный свободный блок хотя бы в 2000 элементов будет невозможно, хотя заполнено всего 0,1% массива из миллиона элементов.
Степень фрагментации слабо коррелирует с количеством занятых элементов. Особенно, если для фрагментации достаточно лишь зарезерировать участок, и даже не нужно выделять страницы памяти. Зарезервированные страницы — это эфемерный ресурс, не ограниченный ничем на уровне всей системы, и ограниченный в рамках каждого процесса размером user-mode части АП этого процесса.
(*) — когда я выше писал, что «ничем не ограничен», это не совсем правда. На самом деле, на поддержание информации о том, какие участки памяти свободны/зарезервированы/выделены тоже нужна память, и в этом смысле всё-таки есть общесистемный и зависящий конфигурации ПК лимит, правда не столько на объём зарезервированных участков, сколько на количество зарезервированных участков.
Mikle писал(а):Возникла идея, не то, чтобы мне это было нужно, просто мысли вслух - если внедрить в IDE некий хук, который сразу при старте будет занимать всю свободную память, кроме, скажем, 200 Мб, а после прогрузки всех зависимостей освобождать эту память, после таких манипуляций сможет ли проект получить доступ к большему объёму памяти одним куском?
Мысль верная, но при старте достаточно именно резервировать, а не выделять этот объём. Другой вопрос, что оставшихся 200 Мб может не хватить на то, чтобы IDE сделала все свои дела при запуске, и тогда она просто не запустится.