Как известно, память можно резервировать (флаг MEM_RESERVE) и непосредственно передавать для использования (MEM_COMMIT). Таким образом, если вначале зарезервировать, скажем, 64Мб памяти, а затем в этом зарезервированном регионе по чуть-чуть выделять память (скажем по 128Кб), то может получится очень неплохая реализация динамических массивов. Имеется ввиду, что вначале выделил 128Кб, записал что-то, если место закончилось, то на конце этого выделенного региона выделяется такой же блок памяти и т.д. Так вот вся проблема в том, что когда это все отработает, то оставшиеся, скажем, 60Мб зарезервированной памяти мне нафиг не нужны (у меня еще 100 таких массивов будет - памяти не хватит), и мне их надо как-то освободить. Так VirtualFree с MEM_RELEASE такой трюк делать не умеет - она не умеет освобождать память в середине зарезервированного региона, а если указывать на начало, то она и выделенным страницам ставит состояние свободных, а мне это как-то не особо улыбается Собственно что в данной ситуации можно сделать? P.S. Можно конечно создавать еще один буфер, уже зная размер данных, и копировать эти данные туда. Но это как-то не по-дзенски. P.P.S. Heap*** и иже с ними не предлагать
кажется как-то мутновато. Как так не умеет? где такое прочитал? VirtualXX - функции работают с витруальной памятью, значит они работают со _страницами_, значит, к примеру, для освобождения памяти нужно передавать адресс кратный странице, обычно это 4Кб (FFF) т.е. ксориш указатель на конец памяти которую нада освободить с FFF, добавляешь 1000h, получается указываешь на начало следующей страници, освобождаешь её и всё последующие А почему хип не предлагать?
имхо rain прав, VirtualAlloc выделяет память страницами дискретно и страницы друг от друга не зависят.. освободить можно любые из них по выбору.
Ну вроде в msdn ясно написано Code (Text): MEM_RELEASE Releases the specified region of pages. After this operation, the pages are in the free state. If you specify this value, dwSize must be 0 (zero), and lpAddress must point to the base address returned by the VirtualAlloc function when the region is reserved. The function fails if either of these conditions is not met.
ниче не понял, но VirtualFree не должна освобождать страницы посередине региона с флагом MEM_RELEASE, только с MEM_DECOMMIT (или как-то вроде)
а mix_mix'у зачем-то нужен именно MEM_RELEASE mix_mix, а может стоит задуматься, действительно ли это так необходимо?
Ну MEM_RELEASE нужен для того, чтобы освободить страницы (они зарезервированные). Ну а насчет другой реализации, это будет несколько сложнее в реализации, да и помедленней, просто мне в голову пришла такая идея, да и вроде бы все работает за исключением одной детали. Ну в принципе, если создавать дополнительный буфер (сразу commit) уже определенного размера, скопировать туда данные, а затем полностью очистить вспомогательный буфер (тот который растущий), то все будет пинцетно. Просто это как-то не очень... Вот я и спрашивал, может как-то можно все сделать без гемора и виндовыми средствами...
А что если зарезервировать сразу метров 512 памяти (виртуальной конечно, а не физической) и не отдавать? Ты можешь commit/decommit любую страницу в этой области, так что напиши собственный менеджер памяти, который и будет размещать твои массивы так, как тебе это надо.
ой, что-то я намутил в прошлом посте, только что проверил, можно и не по страницам освобождать память а хоть и по байтам, просто windows не выгрузит страницу пока не будет вызвана VirtualFree + MEM_DECOMMIT для каждого байта в странице. Сами страници _не_ заризервированны, при MEM_RESERVE резервируется пространсво в АП твоего процесса, т.е. реально страници выделяются только после MEM_COMMIT, и соответственно освобождаются при MEM_DECOMMIT. Убедится в этом можно так: запускаешь эту программку, и открываешь Process Explorer, выбираешь процесс --> Properties --> Perfomance --> VirtualMemory. Запоминаешь значение Private Bytes. Клацаеш ОК, сначала выделяется память, потом начинает освобождаться по 4К, о чём свидетельствует ProcessExplorer :-Ъ Code (Text): .386 .model flat, stdcall option casemap: none include windows.inc include macros.asm include kernel32.inc includelib kernel32.lib include user32.inc includelib user32.lib .data buf db 256 dup (0) .code start: invoke MessageBox, 0, chr$("open process explorer -> VirtualMemory -> private bytes "), 0,MB_OK invoke VirtualAlloc, 0, 30000h, MEM_RESERVE+MEM_COMMIT, PAGE_EXECUTE_READWRITE mov esi, eax mov edi, esi add esi, 30000h invoke wsprintf, offset buf, chr$("reserved from = %x"), eax invoke MessageBox, 0, offset buf,0,MB_OK .repeat sub esi, 1000h invoke VirtualFree, esi, 1000h, MEM_DECOMMIT .if eax invoke wsprintf, offset buf, chr$("released = %x"), esi invoke MessageBox, 0, offset buf,0,MB_OK .endif .until esi<edi ret end start
всу ето звучит дорого ну на дела ето фантастико потому что на win98 60% из функции не работаут понятно и дорого.все изкрват из реклами а на деле ничто.я тоже имеу проблем с буфера памяти когда ползуус GlabalAlloc,Global Lock, потом GlobalUnlock i GlobalFree. моя идея ползоват динамическом разером буфера идет тяжелая для вин98 оф и толко проблеми радоват мои програмисткие ночи. Потом вопрос можно идет о RichEdit.DLL которои нито Rich нито Edit и основнъих функциях у нему не рабоут ,например перехвата адреса текстовои буфер которои нужен для дирекнои работои с него , адрес из функции не возвращается и т.д. вин система для меня изначало на первои взгляд хорошая и на второи - ужас ,смертелнии .((((((((((((
rain Запусти вот этот код под отладчиком: Code (Text): @@: invoke VirtualAlloc, 0, 1024*1024*700, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE invoke VirtualFree, eax, 1024*1024*700, MEM_DECOMMIT jmp @B На третьей итерации цикла VirtualAlloc вернет ERROR_NOT_ENOUGH_MEMORY, несморя на то, что все предыдущие выделенные блоки по 700Мб ты возвращал системе и Process Explorer покажет это же. Вот здесь как раз все дело в том, что эти страницы еще и зарезервированны, поэтому аллокатор неможет их выделить. Я как раз об этом говорю. Наверное, здесь как раз нужно использование менеджера памяти.
mix_mix На 32-битной машине размер виртуальной памяти всего 4 Гб, из которых старшая половина(на отдельных осях четверть) зарезервирована системой. Т.е. юзеру остаётся 0x00000000 - 0x7FFFFFFF Минус образ процесса, образы загруженных dll-ек.....
Ну я знаю что процессу по умолчанию 2Гб виртуальной памяти дается. Я показал rain'у, что резервируя память и не отдавая ее, она (память) рано или поздно закончится.
mix_mix Даже если бы возможность уменьшить размер виртуального блока памяти существовала, она была бы бесполезной . Предназначение VirtualAlloc + MEM_RESERVE обеспечение массиву непрерывного адресного простанства, поэтому если ты нарезервируешь много виртуальных блоков а затем исхитришься поуменьшать их размер, то получишь дико фрагментированное адресное пространство, причём когда ты попытаешься исправить ситуацию "пристроив" второй массив в хвост к первому, то не факт, что тебе это удастся, поскольку промежуток может оказаться занят в результате "безобидных действий" какой нибудь win api В результате будешь иметь полный heap, только собственного изготовления Поэтому резервируй (MEM_RESERVE) сразу большой блок памяти, а затем выделяй в нём реальную память (MEM_COMMIT) для своих массивов (см. про "оторванный блок"). Тогда можешь тасовать свои массивы в зарезервированном пространстве как угодно и быть уверенным в подконтрольности ситуации. Только перемещая массивы не забывай выделять реальную память там куда копируешь массив и освобождать её (MEM_DECOMMIT) там, откуда скопировал Ну и есно не стоит забывать, что из 2Гб юзермодного пространства часть используется win api и им нужно оставить хоть немного места (метров этак 250-500 . Интел начиная 386 камней позволяет перемещать страницы памяти (размером 4к, 2М или 4М) в виртуальном адресном пространстве не копируя их, а только сопоставляя им соответствующий адрес, но к сожалению в юзермоде винды мне удалось найти только достаточно заморочную AWE реализацию этого механизма, которая требует win 2000 или выше и хитрых прав доступа. Если я правильно понял Рихтера и msdn, то выделив блок реальных страниц (AllocateUserPhysicalPages) можно затем подставить его (MapUserPhysicalPages) в любую (кратную размеру страницы) позицию виртуального адресного пространства предварительно зарезервированного через VirtualAlloc + MEM_RESERVE, а затем при желании можно переремапить этот блок на новое место без копирования памяти, а также объединить несколько последовательных блоков в один (MapUserPhysicalPagesScatter) или временно отсоединить реальный массив от адресного пространства чтобы сопоставить адресное окно другому физическому масиву.
Nouzui Ага тоже хорошее решение, правда если бы виртуальная часть FileMapping была реализована в полном объёме (64 битное виртуальное адресное пространство, с плавающим окном в 1-1.5 Гб), то цены бы ей не было ) Но увы размер виртуального файла упирается в 2Гб. Зато можно наоткрывать кучу виртуальных файлов и по очереди подставлять их в окно Code (Text): ; Резервируем 2Гб адресного пространства (это совсем не нагрузит систему :)))) invoke CreateFileMapping, INVALID_HANDLE_VALUE, 0, PAGE_READWRITE or SEC_RESERVE, 0h, 2048*1024*1024, 0 mov [h_map_file_1], eax ; Резервируем ещё 2Гб адресного пространства (это опять не нагрузит систему :)))) invoke CreateFileMapping, INVALID_HANDLE_VALUE, 0, PAGE_READWRITE or SEC_RESERVE, 0h, 2048*1024*1024, 0 mov [h_map_file_2], eax ; Резервируем ещё 2Гб адресного пространства (и это тоже не нагрузит систему :)))) invoke CreateFileMapping, INVALID_HANDLE_VALUE, 0, PAGE_READWRITE or SEC_RESERVE, 0h, 2048*1024*1024, 0 mov [h_map_file_3], eax ; Выделяем 1Гб окно в адресном пространстве нашего процесса и сопоставляем ему начало первого мап-файла invoke MapViewOfFile, [h_map_file_1], FILE_MAP_ALL_ACCESS, 0, 0, 1024*1024*1024 mov [h_mem_win], eax ; Наконец-то выделяем реальную память аж 12кБ :)) invoke VirtualAlloc, [h_mem_win], 12*1024, MEM_COMMIT, PAGE_READWRITE invoke wsprintf, [h_mem_win], zSTR(<'Пробная запись в первую выделенную память', 13, 10>) ; Выделяем "оторванный блок" памяти в середине окна в мап-файл mov edi, 512*1024*1024 add edi, [h_mem_win] invoke VirtualAlloc, edi, 12*1024, MEM_COMMIT, PAGE_READWRITE invoke wsprintf, edi, zSTR(<'Пробная запись в первый оторванный блок', 13, 10>) ; Закрываем окно в первый мап-файл invoke UnmapViewOfFile, [h_mem_win] ; Открываем адресное окно для второго Гб второго мап-файла invoke MapViewOfFile, [h_map_file_2], FILE_MAP_ALL_ACCESS, 0, 1024*1024*1024, 1024*1024*1024 mov [h_mem_win],eax ; И в нём тоже выделяем реальную память invoke VirtualAlloc, [h_mem_win], 12*1024, MEM_COMMIT, PAGE_READWRITE invoke wsprintf, [h_mem_win], zSTR(<'Пробная запись во вторую выделенную память', 13, 10>) ; Закрываем окно во второй мап-файл invoke UnmapViewOfFile, [h_mem_win] ; Считываем данные для проверки ; Опять открываем адресное окно для первого мап-файла invoke MapViewOfFile, [h_map_file_1], FILE_MAP_ALL_ACCESS, 0, 0, 1024*1024*1024 mov [h_mem_win], eax ; считываем из него первый блок данных mov esi, offset zsResult invoke wsprintf, esi, [h_mem_win] add esi, eax ; считываем из него "оторванный" блок данных mov edi, 512*1024*1024 add edi, [h_mem_win] invoke wsprintf, esi, edi add esi, eax ; Закрываем окно в первый мап-файл invoke UnmapViewOfFile, [h_mem_win] ; Открываем адресное окно для второго мап-файла invoke MapViewOfFile, [h_map_file_2], FILE_MAP_ALL_ACCESS, 0, 1024*1024*1024, 1024*1024*1024 mov [h_mem_win], eax ; считываем из него второй блок данных invoke wsprintf, esi, [h_mem_win]) invoke MessageBox, NULL, addr zsResult, zSTR(<'Я изучаю Win32', 21h>), MB_OK Правда сработало это только в ХР, а в 98й всё это безобразие ведёт себя совершенно неадекватно