Правильное освобождение выделенной памяти

Тема в разделе "WASM.WIN32", создана пользователем EvilsInterrupt, 20 фев 2005.

  1. EvilsInterrupt

    EvilsInterrupt Постигающий азы дзена

    Публикаций:
    0
    Регистрация:
    28 окт 2003
    Сообщения:
    2.428
    Адрес:
    Russia
    Нужно:

    1. Выделить память на n байтов

    2. получить указатель на эту память

    3. Чтобы при необходимости юзать память ч\з указатель

    4. освободить память



    как я понял мои действия должны быть:

    local hMem : DWORD

    LOCAL PMem : DWORD



    invoke GlobalAlloc,атрбут,n(байтов)

    mov hMem,eax

    invoke GlobalLOck,hMem

    mov pMem,eax

    ..

    юзаю память

    ..

    invoke GlobalFree,hMem



    Это все что я сделать для освобождения памяти?

    Или же я еще, чтото должен сделать для корректности?!



    зы: весь код будет находиться в виде api в dll и пользо-

    ватель будет передавать скоко нужно памяти в виде числа

    n. Ессно дело оно можеть быть как 512, так и 12220, а

    может и не эти цифры!
     
  2. Asterix

    Asterix New Member

    Публикаций:
    0
    Регистрация:
    25 фев 2003
    Сообщения:
    3.576
    VirtualAlloc

    VirtualFree
     
  3. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Необходимость GlobalLock зависит от флагов, установленных при вызове GlobalAlloc. При GMEM_FIXED возвращается сразу поинтер. Необходимости в GlobalLock нет.

    Если GlobalLock использовалась, надо затем вызывать GlobalUnlock, чего в твоём коде нет.

    А вообще подробней все описано в msdn Platform SDK: Memory Management с примерами
     
  4. EvilsInterrupt

    EvilsInterrupt Постигающий азы дзена

    Публикаций:
    0
    Регистрация:
    28 окт 2003
    Сообщения:
    2.428
    Адрес:
    Russia
    Спасибо!
     
  5. EvilsInterrupt

    EvilsInterrupt Постигающий азы дзена

    Публикаций:
    0
    Регистрация:
    28 окт 2003
    Сообщения:
    2.428
    Адрес:
    Russia
    cresta



    Про Unlock заметил раньше до поста, но с моим знанием Английского за сомневался...



    Тогда возникает вопрос, а стоит ли GmemFixed или же продоставить винде возможность перемещать блок, возникает трудность с удержанием корректности указателя
     
  6. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Если даже и GMEM_MOVEABLE был сделан, то перемещается только в пределах твоей кучи. О том, что указатель будет меняться, а если меняется - то как - ни слова не нашёл в описании GlobalAlloc.



    Вообще-то GlobalAlloc и иже с ним - функции устаревшие. Рекомендуется, как выше заметил Asterix,

    - VirtualAlloc
     
  7. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    EvilsInterrupt

    Порядок работы с перемещаемой памятью такой:

    1. Выделяешь блок памяти функцией GlobalAlloc;

    2. Блокируешь этот блок с помощью функции GlobalLock. Эта функция возвращает тебе адрес блока в памяти. Пока блок заблокирован его адрес в памяти измениться не может;

    3. Юзаешь этот блок;

    4. Разблокируешь блок с помощью функции GlobalUnlock. С этого момента адрес блока в памяти становится не действительным. Если потребуется снова заюзать этот блок, то потребуется вернуться к п.2 этого списка;

    5. Освобождаешь память с помощью функции GlobalFree.



    Вот и все, и нет никаких трудностей с "удержанием корректности указателя".



    З.Ы.: Как тебе уже советовали, юзай VirtualAlloc/VirtualFree!!!
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Вот достали с этим GlobalAlloc ...



    EvilsInterrupt, тебе же как-то русскими словами обясняли (помнится что-то там про ООП вопрос был):



    1) GlobalAlloc устарела. Использовать ее можно, но только с флагом GMEM_FIXED. А вот GMEM_MOVEABLE вообще в Win32 ИГНОРИРУЕТСЯ, поэтому использовать moveable и lock\unlock в новых прогах - просто бессмысленно (это только для обратной совместимости). Ну нету в Win32 такого понятия "перемещаемая память", н-е-ету. Физическая память сама по себе, а виртуальные адреса сами по себе. При перемещении физической памяти, виртуальные адреса не изменяются.



    2) Вместо GlobalXXX в Win32 используются HeapAlloc, HeapRealloc, HeapFree c параметром hHeap = GetProcessHeap.

    (Кстати, никакого намека на moveable в этих функциях нет, т.к. этого понятия теперь действительно нет)



    3) Можно использовать и VirtualAlloc, но с умом, т.к. она резервирует адресное пространство с дискретом в 64К и выделяет память страницами по 4К. Поэтому использовать ее для "мелочевки" просто неразумно - для этого есть HeapAlloc.
     
  9. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    leo





    А как сейчас оси семейства Win32 решают проблему фрагментации памяти?
     
  10. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Oleg_SK





    Олег. Это не возможно - потому что оптимально использовать память может только тот, кто знает о том, для чего она используется.



    А что ОС знает об использовании твоей памяти?

    Очень немного.



    Выделять память у ОС - оптимально большими обёмами. А расперделять её - лучше самому.
     
  11. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    Edmond

    А разве перемещаемая память не решала эту проблему (хотя бы частично)? Если решала, то почему Microsoft отказалась от ее потдержки? Если не решала, то хотелось бы узнать: почему?
     
  12. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Oleg_SK

    > "А как сейчас оси семейства Win32 решают проблему фрагментации памяти?"



    Какой памяти - физической или виртуальных 2Гб памяти процесса ?



    Работа с физической памятью осуществляется на уровне страниц. Винда может выкинуть часть страниц в файл подкачи и затем загрузить их в ОЗУ по другим адресам. Но делается это все "незаметно" для нашего процесса, т.к. новые физ.адреса транслируются на прежние виртуальные адреса процесса и мы не замечаеи никаких перемещений. Поэтому никакие локи\анлоки и не нужны.



    Что касается виртуальных адресов, то это проблема программера, а не винды (по большому счету это не ее дело, она может лишь предложить некоторые функции). Для этого и существуют менеджеры динамической памяти, которые худо-бедно решают проблему фрагментации и "оптимального" использования физической памяти и виртуальных адресов. Реально в нетривиальных задачах куча всегда фрагментирована, важно лишь чтобы не происходило катастрофическое разрастание ее объема. Для этого менеджер ведет учет свободных блоков, по возможности осуществляя их слияние и укрупнение, и при новом запросе HeapAlloc старается выделить память на месте одного из свободных блоков.



    Поэтому к добродушным "рекомендациям" напрямую использовать VirtualAlloc следует относиться осторожно - если выделил и освободил, то ничего страшного. Но Heap придумана не зря, т.к. для всех случаев жизни VirtualAlloc не годится из-за большой гранулярности резервирования и выделения памяти. Если ее использовать вместо HeapAlloc для выделения большого числа мелких блоков с произвольным размером и порядком выделения\освобождения, то ничего хорошего из этого не получится (если конечно не городить собственный менеджер).
     
  13. Edmond

    Edmond узник замка IF THEN ELSE

    Публикаций:
    0
    Регистрация:
    2 сен 2002
    Сообщения:
    203
    Адрес:
    WASM.RU
    Oleg_SK

    Потому что вы не читали новую часть УП (то есть новую 1 часть) :)

    Кину вам её на мыло...
     
  14. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    leo



    Я имел ввиду фрагментацию адресного пространства виртуальной памяти. А разве в среде Win32 существует проблема фрагментации адресного пространства физической памяти? Я первый раз об этом слышу. Нет, я конечно знаю что оно фрагментировано, но IMHO это скорее норма чем проблема (что и проиллюстрировано в вашем ответе)... Если я не прав, то поправьте меня.







    Да, это понятно. Я кивнул на API-функции VirtualAlloc/VirtualFree потому, что EvilsInterrupt сказал:



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



    Из вашего ответа я понял, что решением проблемы фрагментации адресного пространства виртуальной памяти занимается только менеджер куч (да и то, только частично).

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



    Edmond



    :) Спасибо! Будет интересно почитать... Буду с нетерпением ждать этого!
     
  15. Oleg_SK

    Oleg_SK Guest

    Публикаций:
    0
    leo



    Т.е. вы имеете в виду, что если программер столкнется с этой проблемой при работе с не маленькими блоками памяти, то ему нужно будет делать свой менеджер памяти? Я правильно понял?
     
  16. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Oleg_SK



    1) > "А разве в среде Win32 существует проблема фрагментации адресного пространства физической памяти? Я первый раз об этом слышу. Нет, я конечно знаю что оно фрагментировано, но IMHO это скорее норма чем проблема"



    Поскольку физическая память выделяется страницами, то есть "крупная" фрагменация памяти на уровне страниц (которая есть норма) и есть "мелкая" фрагментация внутри выделенных страниц. Внутристраничная фрагментация является общим "злом" и для физической и для виртуальной памяти. Если мы сделаем два VirtualAlloc(0,512,MEM_COMMIT,..), то "скушаем" 128К резерва адресов и 8К физической памяти (после первого чтения\записи), хотя реально нам нужен 1К. Остальные 7K - мертвым грузом коту под хвост. ИМХО в разумных пределах это тоже норма, но ученикам Iczelion'a это не мешало бы напомнить, прежде чем безоговорочно советовать использовать VirtualXXX вместо HeapXXX.



    2) > "в то время как механизм кучи ориентирован на управление не большими блоками..."



    Что значит ориентирован ? Механизм кучи может и должен управлять любыми блоками (насколько эффективно - другой вопрос). Просто маленькие блоки (до 4К) составляют основную проблему при освобождении физ.памяти, поэтому к ним особый подход (хранится массив списков свободных блоков "всех" размеров с заданной гранулярностью). Средние блоки занимают несколько страниц, соответсвенно при их освобождении можно сразу делать DECOMMIT выделенных страниц (хотя как правило менеджеры не "мелочатся" и освобождают физ.память блоками по несколько страниц). Но со средними блоками сложнее обстоит дело с поиском блока подходящего размера (как правило, они все хранятся в одном списке -> дилема: быстродействие или фрагментация). С большими блокам вообще все просто - если IS_BIG_SIZE, то напрямую вызывается VirtualAlloc или VirtualFree. Правда в Win95\98 такой фичи похоже нет, поэтому микрософт в 9х рекомендует использовать VirtualXXX для блоков более 1-2Мб. Каков размер IS_BIG_SIZE в NT-based OS я не знаю (для справки: для Windows CE согласно MSDN это 192K, а в борландовских менеджерах C++ и Delphi это 1Мб).



    3) > "решением проблемы фрагментации адресного пространства виртуальной памяти занимается только менеджер куч (да и то, только частично)"



    Это проблема программера, который должен правильно организовать выделение и освобождение блоков адресного пространства и физ.памяти. А помогают ему в этом менеджеры памяти - либо API-шные, либо от других разработчиков, либо свой собственный - полная свобода выбора. ОСи до этого дела нет, ее задача организовать устойчивую бесконфликтную работу нескольких приложений. Поэтому, если у рядового приложения возникнут внутренние проблемы, с которыми оно не может справиться, ось его просто прикончит, чтобы не мешалось. "Если ошибка будет повторяться, обратитесь к разработчику" - знакомая фраза ?



    4) > "Причина, по которой не реализован механизм перемещаемой памяти так и осталась загадкой"



    Ну, во-первых, мне достоверно не известно реализован он или нет, и если реализован то как. Микрософты мутят воду, но по логике получается, что если и реализован, то на "всякий случай". Логика простая. Микрософты признают (практически дословно), что GlobalXXX сохранены для обратной совместимости c 16-бит Windows, работают медленнее других функций управления памятью и не обеспечивают их возможностей, поэтому новым приложениям следует использовать HeapXXX. В тоже время "Следующие функции эквивалентны: GlobalAlloc, LocalAlloc, and HeapAlloc with the handle returned by the GetProcessHeap function". Однако в функциях HeapXXX и VirtualXXX мы не находим никакого намека на MOVEABLE, откуда делаем заключение, что эта фича для "новых" приложений не поддерживается, а значит нет смысла ее поддерживать и для "старых" GlobalXXX, т.к. для совместимости достаточно только имитировать работу lock\unlock, т.е. делать инкремент\декремент числа lock\unlock и при LockCount = 0 делать вид, что "все ОК ребята, вы не зря трудились", теперь ваш блок может быть перемещен и вы можете не беспокоится о фрагментации кучи. Но вот переместится ли он когда нибудь - это большой вапрос. В описании флага GMEM_MOVEABLE читаем, что "в Win32 блоки памяти никогда не перемещаются в физ.памяти, но они могут могут быть перемещены внутри основной кучи". Что это означает в свете страничной организации памяти ? Не что иное как перетрансляцию физ.страниц в новые виртуальные адреса, т.е. перемещаться могут только целые страницы. Легко представить насколько это непростая задача с негарантированным успехом - анализировать состояние кучи в поисках moveable+unlock страниц и свободного места, куда их можно перетранслировать. Кто может гарантировать, что на той же странице где расположен moveable блок (или его часть) не сидит fixed или locked блок. Если страница заполнена мелкими блоками, то для возможности перемещения все эти блоки должны быть в состоянии moveable+unlock. Ясно, что ни о каком "фоновом" режиме перемещения и речи быть не может - вся эта фича только на аварийный, пожарный случай. Единственная надежда на принудительный вызов HeapCompact, хотя в ее описании ни слова не сказано о перемещаемых блоках - только попытка укрупнения свободных блоков и decommit свободных страниц. Отсюда можно сделать вывод, что если механизм перемещаемой памяти и реализован, то простые локи\анлоки от фрагментации кучи наврядли спасут - нужно либо ждать аварийного предупреждения от системы о нехватке памяти, либо периодически контролировать GlobalMemoryStatus и надеятся на HeapCompact, которая попытается (attempts) что-то сделать.



    А вот в отличие от механизма реализации перемещения, о механизме поддержки этой устаревшей фичи известно больше. И приверженцам GMEM_MOVEBLE не мешало бы знать о том, что на эту ненужную фигню при первом вызове резервируется 512К (65536 хэндлов по 8 байт) вашего драгоценного адресного пространства и выделяется 4К драгоценной физ.памяти. Поэтому для простеньких задач (типа выделил-удалил) - использование GMEM_MOVEBLE выглядит просто глупо.