Что такое код allocmem

Содержание

В чем разница между GetMem и AllocMem?

В Delphi я вижу несколько похожих функций, которые можно использовать для выделения памяти, например, GetMem и AllocMem. Каковы различия между ними?

Я прочитал документ и обнаружил, что GetMem не будет инициализировать память после выделения, в то время как AllocMem делает.

Тогда нужно ли мне инициализировать память после вызова GetMem? Док говорит да. Но я вижу в некоторых исходных кодах Delphi они не вызывают Initialize.

И нужно ли мне дорабатывать память после ее окончания? Я вижу в некоторых исходных кодах Delphi, они делают, но кое-что они не делают.

Логика кажется простой — если вам нужен буфер с нулевой инициализацией, вы можете использовать AllocMem .

Если в любом случае вы заполняете буфер собственными данными и никогда не используете содержимое по умолчанию — вы можете использовать GetMem .

Разница в том, что AllocMem заполняет вновь выделенный буфер нулями, а GetMem — нет. Если ваш код требует, чтобы вновь выделенный буфер изначально был полностью нулем, вы можете использовать AllocMem вместо ручной записи нулей в буфер; если вам не GetMem начальные байты в буфере, вы можете сделать (возможно) более дешевый GetMem .

допустимо и всегда будет отображать 50 , но

может отображать что угодно — все зависит от того, какой байт находился в точке p + 1 во время выполнения кода (случайность).

Если вы начнете с заполнения буфера нулями, как в

вы гарантированно увидите 20 , так как p + 1 будет держать 0 .

В качестве альтернативы вы могли бы сделать

поскольку документация гарантирует, что AllocMem устанавливает каждый байт во вновь выделенном буфере в 0 .

Но, конечно, выделение памяти вручную в куче для («продвинутых») вещей низкого уровня; чаще всего вы этого не делаете. Если вы это сделаете, вы должны знать о вещах, таких как внутренние форматы данных.

Зависит от вашей необходимости. Вам нужен только буфер, и вам все равно, что у него изначально? Используйте GetMem.

GetMem выделяет блок данного размера в куче и возвращает адрес этой памяти в параметре P. Байты выделенного буфера не установлены в ноль. Чтобы избавиться от буфера, используйте FreeMem. Если для выделения блока недостаточно памяти, возникает исключение EOutOfMemory.

Примечание. Если память должна быть инициализирована нулями, используйте вместо нее AllocMem.

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

AllocMem выделяет блок данного размера в куче и возвращает адрес этой памяти. Каждый байт в выделенном буфере устанавливается в ноль. Чтобы избавиться от буфера, используйте FreeMem. Если для выделения блока недостаточно памяти, возникает исключение EOutOfMemory.

Примечание. Если память не требует инициализации нулями, более эффективно использовать вместо нее GetMem.

//А нужно ли мне доработать память после ее окончания? Если говорить о распределении памяти вообще, то память, которую вы выделяете, всегда должна быть освобождена.

Есть несколько исключений из этого —

  • Когда ваши объекты подсчитываются
  • Когда другой объект заботится об объекте и освобождает его

Как перехватить вызовы AllocMem (), FreeMem () и т. Д.? Это может быть любой вид техники dll, исполняемый файл и т. Д.

Я пытаюсь перехватить события, связанные с выделением памяти, чтобы создать внешний отладчик, который не нуждается в дополнительной реализации исходного кода. Для этого мне нужно перехватить эти звонки, кто-нибудь знает, как это сделать? Что-то вроде http://www.itworld.com/UIR000929interposers, но это работает и на Windows. Реализации и идеи на C / C ++ тоже приветствуются.

Ответы за ответы, я сделаю свои тесты, чтобы найти лучшую альтернативу.

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

Возможно, вы захотите проверить в обход Microsoft. Книги Джеффри Рихтера по программированию для Windows содержат похожую библиотеку.

Каждый компилятор имеет свою собственную систему управления памятью. И, если быть точным, у вас может быть несколько ММ, работающих на одном компиляторе. Вы можете выбрать, какой MM использовать, в зависимости от цели вашего приложения. Например, на сервере вы можете быть заинтересованы в многопоточном масштабирующем MM, но в простом клиентском приложении пользовательского интерфейса вы хотите, чтобы ваш MM был быстрым и не занимал память.

Внутреннее управление кучей, предоставляемое Windows, было настолько медленным (по крайней мере, до XP), что каждый компилятор / инфраструктура реализовал свой собственный менеджер памяти.

Для Delphi с 2006 года существовала «чистая Borland» версия, затем в основную IDE «Delphi» был включен MM с открытым исходным кодом FastMM4 .

С Delphi очень легко изменить менеджер памяти. Вы просто создаете запись функций, а затем вызываете SetMemoryManager (), чтобы заменить текущий MM новым.

Например, вот как устанавливается наш Диспетчер памяти с открытым исходным кодом для Delphi:

Этот код заменит Delphi MM нашими собственными, через пользовательские функции Scale_GetMem / Scale_FreeMem / Scale_ReallocMem / Scale_AllocMem . Вы можете просто создать оболочку для старого MM, используя переменную OldMM :

Структура записи ММ изменялась во времени, поэтому вам нужно будет выбрать правильную — мы делаем это, используя условное USEMEMMANAGEREX .

AllocMem Function

The AllocMem callback function allocates memory from the process heap to a buffer. Any memory allocated with this function will automatically be freed by IIS when the session ends.

Parameters

pfc
Points to the HTTP_FILTER_CONTEXT Structure that is associated with the current, active HTTP transaction.

cbSize
Indicates the size of the buffer to be allocated, in bytes.

dwReserved
Reserved for use by the server, and should be set to 0.

Return Values

Returns true if the function was successful; otherwise false. The GetLastError function indicates the reason for failure. The most common reason is as follows:

If pfc or the server context that pfc points to is invalid. Additionally, if the header name does not end with a colon (:).

Remarks

You can call AllocMem as many times as your code requires, but you cannot flush the AllocMem buffers. IIS maintains a list of allocated memory blocks created in the filter context and frees them after a SF_NOTIFY_END_OF_NET_SESSION notification. This means that the lifespan of AllocMem memory blocks is associated with the connection to the client, not with a particular request. If you are allocating a lot of memory with a Keep-Alive connection, then none of those blocks will be freed until the client is no longer connected.

As an alternative, if blocks of memory are only needed «per request», then you can use whatever memory allocation function that you like and clean up after the SF_NOTIFY_END_OF_REQUEST notification. However, it is easy to lose track of memory blocks that you have allocated if you don’t fully understand when notifications fire. See Event Notification Order before you attempt to create memory blocks without using the AllocMem callback function.

If you use AllocMem, the tail bytes of the memory block are filled with a signature that the heap can check at de-allocation time to see if the memory blocks were modified. If they were modified, a heap corruption error is returned that looks like the following:

Heap block at modified

To debug this error, use the following procedure

Enable pageheap for the Inetinfo.exe process by typing text at the command prompt and pressing ENTER:

Stop the Inetinfo.exe process by typing the following:

Restart the Inetinfo.exe process by typing the following:

Attach a debugger to the Inetinfo.exe process and rerun your filter. An Access Violation (AV) should occur right at the moment unauthorized code overwrites the memory block. Check the call stack of the thread that has the AV.

When you are done debugging, turn pageheap off for the Inetinfo.exe process by typing the following:

Then stop and start the Inetinfo.exe process as above. Pageheap reduces the performance of your Web server if you leave it enabled.

Requirements

Client: Requires Windows XP Professional, Windows 2000 Professional, or Windows NT Workstation 4.0.

Server: Requires Windows Server 2003, Windows 2000 Server, or Windows NT Server 4.0.

Как подключить AllocMem (), FreeMem () и т.д. . звонки? Это может быть любым видом техники DLL, исполняемый файл, и т.д.

Я пытаюсь зацепить события , связанные с выделением памяти для создания внешнего отладчика , которые не имеют никакой необходимости дополнительной реализации на исходном коде. Для этого мне нужно подключить тезисы звонков, некоторые один знают , как это сделать? Нечто подобное, http://www.itworld.com/UIR000929interposers но это работает на окнах тоже. C / ++ реализации или идеи C приветствуются тоже.

Tks за ответы, я буду делать свои тесты, чтобы найти лучший вариант.

Я проверка ответов нет доступных решений в настоящее время. Я ищу и исследование моей личности тоже. Если открытие я некоторые вещи нового я выложу здесь.

Каждый компилятор имеет свою собственную систему управления памятью. И, чтобы быть точным, вы можете иметь несколько ММ работает над тем же компилятором. Вы можете выбрать MM использовать, в зависимости от цели применения. Например, в сервере вы можете быть заинтересованы в многопоточном масштабировании ММ, но на простом приложении UI клиента, вы хотите, чтобы ваше ММ просто быть быстрым и не потреблением памяти.

Внутреннее управление Heap обеспечивается Windows, так медленно (по крайней мере, до XP), что каждый компилятор / рамки действительно реализовать свой собственный менеджер памяти.

Для Delphi, был «чистым Борланд» версия с 2006 года, затем Open Source MM, названный FastMM4 , был включен в основной «Delphi» IDE.

В Delphi это очень легко изменить менеджер памяти. Вы просто создать запись функций, а затем вызвать SetMemoryManager (), чтобы заменить текущую ММ с новым.

Этот код заменит Delphi MM нашей собственной, с помощью пользовательских Scale_GetMem / Scale_FreeMem / Scale_ReallocMem / Scale_AllocMem функций. Вы можете просто сделать обертку к старому ММ с использованием OldMM переменной:

Структура записи ММ меняется во времени, так что вы должны выбрать правое одно — мы делаем это с помощью USEMEMMANAGEREX условной.

What is the difference between GetMem and AllocMem?

In Delphi, I see several similar functions that can be used to allocate memory, such as GetMem and AllocMem. What are the differences between them?

I read the document and only find that GetMem will not initialize the memory after allocation, while AllocMem does.

Then whether I need to initialize the memory after calling GetMem? The doc says yes. But I see in some Delphi source code they do not call Initialize.

And whether I need to finalize the memory after finishing using it? I see in some Delphi source codes, they do, but somethings they don’t do.

3 Answers 3

Logic seems simple — if you need zero-initialized buffer, you can use AllocMem .

If you fill buffer with own data in any case, and never use default contents — you can use GetMem .

The difference is that AllocMem fills the newly allocated buffer with zeros, while GetMem doesn’t. If your code requires the newly allocated buffer to be all-zeros initially, you can use AllocMem instead of manually writing zeroes in the buffer; if you don’t care about the initial bytes in the buffer, you can do a (probably) cheaper GetMem .

is valid and will always display 50 , but

can display anything — it all depends on what byte happened to be at p + 1 at the time the code was executed (chance).

If you start by filling your buffer with zeroes, as in

you are guaranteed to see 20 , since p + 1 will hold 0 .

As an alternative, you could do

since the documentation guarantees that AllocMem sets every byte in the newly-allocated buffer to 0 .

But of course, allocating memory manually on the heap is for («advanced») low-level stuff; most often, you don’t do that. If you do that, you should be aware of things like internal data formats.

Depends on your need. Do you need just a buffer and won’t care what it has initially? Use GetMem.

GetMem allocates a block of the given Size on the heap, and returns the address of this memory in parameter P. The bytes of the allocated buffer are not set to zero. To dispose of the buffer, use FreeMem. If there isn’t enough memory available to allocate the block, an EOutOfMemory exception is raised.

Note: If the memory needs to be zero-initialized, use AllocMem instead.

If your logic expects all the bytes of that buffer set to be zero, use AllocMem.

AllocMem allocates a block of the given Size on the heap, and returns the address of this memory. Each byte in the allocated buffer is set to zero. To dispose of the buffer, use FreeMem. If there isn’t enough memory available to allocate the block, an EOutOfMemory exception is raised.

Note: If the memory does not need to be zero-initialized, it is more efficient to use GetMem instead.

//And whether I need to finalize the memory after finishing using it? When talking about allocation of memory generally, the memory you allocate should always be freed.

There are few exceptions to this —

  • When your objects are reference counted
  • When another object takes care of an object and freeing it up
Илон Маск рекомендует:  Что такое код ccvs_add

Блог GunSmoker-а

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

25 апреля 2011 г.

Архитектура памяти в Windows: мифы и легенды (spin-off)

Этот пост — несколько необычное ответвление (spin-off) предыдущего.

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

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

Содержание

Миф №1: программа не может выделить больше памяти, чем установлено ОЗУ

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

Этот миф легко разрушить непосредственным экспериментом. Я установил количество ОЗУ для виртуальной машины в 256 Мб, запустил её и выполнил такой код:
Эта операция будет успешна (хотя и достаточно медленна). Операция была бы мгновенной, если бы мы использовали только резервирование (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 Гб виртуальной памяти. Вы можете выделять память, без проецирования её в ваше виртуальное адресное пространство:
При условии, что у вас достаточно физической памяти и/или файла подкачки, этот запрос на выделение 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 для нашей программы:
Или ( 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? Но не так быстро!

Создадим такую программу:
Эта программа пытается исчерпать память кусками по 64 Кб. Кроме того, она держит резерв памяти, чтобы выполнить WriteLn и работу со строками в конце (в самом деле, если вы исчерпаете всю память, то не сможете вывести результат). Программа также помечена флагом IMAGE_FILE_LARGE_ADDRESS_AWARE , что даёт ей доступ к памяти больше 2 Гб.

Теперь запустим эту 32-х разрядную программу на 64 -х разрядной системе (никаких дополнительных действий вроде включения спец. режимов не требуется):

Это ж без малого аж 4 Гб для 32-х разрядной программы! Т.е. почти двукратное увеличение по сравнению с обычными 2 Гб. Круто.

Статус мифа: plausible .

Миф №8: 32-х разрядная операционная система не может использовать все 4 Гб оперативной памяти

Такое ограничение связано с тем, что в более мощных системах структуры, применяемые диспетчером памяти для отслеживания физической памяти, потребляли бы слишком большую часть пространства виртуальных адресов. Диспетчер памяти отслеживает страницы памяти при помощи массива, называемого базой данных PFN, и в целях оптимизации производительности отображает все содержимое этой базы в виртуальную память. Так как каждая страница памяти представлена структурой данных объемом 28 байт, в системе с физической памятью емкостью 128 Гб для размещения базы данных PFN потребуется 930 Мб. В 32 -разрядных ОС Windows предусмотрено пространство виртуальных адресов объемом 4 Гб, зависящее от оборудования и по умолчанию распределяемое между текущим процессом пользовательского режима (например, блокнотом) и системой. В таких условиях база данных PFN объемом 980 Мб занимает почти половину из доступных 2 Гб системной части пространства виртуальных адресов, а значит, на отображение ядра, драйверов устройств, системного кэша и других структур данных системы остается всего 1 Гб:

По той же причине в таблице ограничений объема памяти указаны пониженные лимиты при загрузке в режиме /3GB . Дело в том, что для этого режима характерна такая схема разделения физической памяти, при которой процессам пользовательского режима достается 3 Гб, а системе – всего 1 Гб. В целях повышения производительности в ОС Windows Server 2008 для системных нужд резервируется более значимая доля адресного пространства. Для этого максимальный объем физической памяти, поддерживаемый в 32 -разрядных версиях ОС, сокращается до 64 Гб.

Илон Маск рекомендует:  Что такое код localeconv

Но разрушители легенд не были бы разрушителями легенд, если бы они верили на слово. Поэтому они должны это проверить.

Берём виртуальную машину, устанавливаем ей количество ОЗУ в 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 система действует так:

  1. Отыскивает ЕХЕ-файл, указанный при вызове функции CreateProcess ;
  2. Создает новый объект ядра «процесс»;
  3. Создает адресное пространство нового процесса;
  4. Резервирует регион адресного пространства — такой, чтобы в него поместил ся данный ЕХЕ-файл. Желательное расположение этого региона указывается внутри самого ЕХЕ-файла. По умолчанию базовый адрес ЕХЕ-файла — $00400000.
  5. Отмечает, что физическая память, связанная с зарезервированным регионом, — ЕХЕ-файл на диске, а не страничный файл.

Спроецировав ЕХЕ-файл на адресное пространство процесса, система обращается к разделу ЕХЕ-файла со списком 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 Кб в условиях нехватки памяти (выделения 2×512 Мб на системе с 256 Мб ОЗУ). И заметьте, что даже при выделении 1 Гб памяти, песочница вашей программы осталась очень компактной — менее 2 Мб: потому что к этой памяти мы не обращались. Мы её только выделили.

Илон Маск рекомендует:  Что такое код iis #echo

Статус мифа: busted .

Миф №12: доступ к невыделенной памяти приводит к возбуждению Access Violation

Гм, разве каждый ребёнок не знает про то, что прежде чем использовать память, её надо выделить? Попытка доступа к невыделенной памяти неизменно закончится ошибкой доступа к памяти. Звучит разумно и миф кажется правдоподобным. Но давайте посмотрим, так ли это на самом деле: Чтобы исключить влияние менеджера памяти 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-е, а вторая — её освобождать: Запустим программу и попробуем щёлкать на кнопках со значением 10240 ( 10 Мб).

Ну, при выделении памяти потребление виртуальной памяти приложением подскакивает на +10 Мб, а при освобождении — уменьшается.

Миф подтверждён? Мы так легко не сдаёмся: попробуйте повторить этот же эксперимент, указывая значения вроде 1 или 4 . Теперь вы можете заметить, что при освобождении памяти, занятая виртуальная память не изменяется. Более того, если вам достаточно повезёт, то вы увидите, что при выделении памяти, потребление виртуальной памяти не увеличивается!

(примечание: это плавающее поведение; возможно, вам придётся поэкспериментировать с выделением/освобождением памяти, прежде чем вы его воспроизведёте)

Неужели мы открыли неизвестный науке принцип возникновения памяти из ничего? Вовсе нет — вспомните, что работа с памятью в Delphi (да и других языках тоже) идёт через менеджер памяти — это прослойка между вами и системой, которая, грубо говоря, упаковывает ваши запросы на память в один пакет. В предыдущем мифе мы уже увидели, что при выделении всего 1 Кб памяти на деле расходуется в несколько раз больше памяти — из-за её гранулярности. Чтобы память не пропадала зря, менеджер памяти располагает в одном блоке памяти сразу несколько ваших запросов на память — вот почему потребление памяти может не изменяться при выделении/освобождении памяти: потому что память будет «выделена» в уже существующем блоке памяти, либо же при освобождении памяти менеджер памяти не сможет освободить блок памяти, потому что там есть и другие занятые регионы (либо он может просто придержать свободный блок, на случай, если вы сейчас захотите заново выделить память).

Заметьте, что это не является какой-то «плохой» вещью, как вам может показаться. Мы уже разрушили такой миф: вспомните, что потребление оперативной памяти программы крайне слабо связано с выделением памяти в ней. Вы можете выделить 1 Гб памяти, но в оперативной памяти система даст вам всего 2 Мб. Так и с этой, временно не используемой памятью: она никак не мешается и лежит в файле подкачки, пока вам не понадобится или пока её не освободят.

Конечно же, сказанное не означает утечку памяти — вся эта память будет в итоге возвращена системе. Либо с течением времени, либо при последующих освобождениях памяти. Так что в целом, на длительном участке, миф выполняется: освобождение памяти в вашем коде уменьшает потребление памяти программой, но это может быть не так локально, на достаточно малых участках кода.

Статус мифа: plausible .

Примечание: предыдущие два мифа приводят к неизбежному заключению: вы не можете судить о том, валиден (т.е. допустим, корректен) ли данный вам указатель (т.е. была ли под него выделена память) или нет. Более того, это нельзя сделать даже при отсутствии факторов, про которые говорится в мифах: Валиден ли P ? Логически — нет, т.к. у нас есть явное освобождение памяти. Но любая проверка покажет вам, что он допустим: потому что память под N выделится ровно на том же самом месте, где были данные от P . Таким образом, P и N будут указывать на одно и то же место в памяти, даже хотя P более не считается допустимым по этому коду. Поскольку вы не можете гарантировать неосуществимость этой операции на практике (за исключением специальных отладочных сборок, конечно же), то вы и не можете судить о допустимости указателя, основываясь на любых его проверках: какую-бы проверку вы ни предложили бы, всегда найдётся ситуация, когда проверка будет давать ложно-положительный результат.

Поэтому, когда вы освобождаете память, всегда присваивайте указателю nil : тогда его проверка на допустимость будет тривиальной if Assigned(P) then .

Миф №14: Obj.Free не приводит к Obj = nil

Если вы работали с объектами, то знаете, что одним из способов удалить объект — это вызывать метод Free , который проверит ссылку объекта и вызовет его деструктор. Правда ли, что после этого ссылка объекта не изменяется и продолжает указывать на бывший объект?

Это очень легко проверить: Запустите её — и вы получите сообщение.

Почему так происходит? Ну, об этом можно догадаться. Free — это метод объекта. Да, в него передаётся указатель на объект, как и в любой другой метод (далее, в методе, этот указатель становится Self ) — но передаётся по значению. Иными словами, Self := nil внутри Free не изменит O — ведь любые изменения в параметре, переданном по значению, не влияют на исходное значение параметра. Free не может изменить O даже теоретически.

Если бы это было не так, то вызов конструктора для создания объекта мог бы быть таким: Если бы изменения в Self влияли бы на исходное значение, то подобный вызов мог бы создать объект и записать ссылку в O . Но вместо этого мы пишем: Что означает создание объекта и запись ссылки в переменную.

Так же и с освобождением объекта: если вы хотите об-nil-ить ссылку — передавайте её по ссылке (в FreeAndNil ): FreeAndNil освободит объект и присвоит O в nil . Я уже упоминал, что FreeAndNil является самым правильным вариантом освобождения объекта из трёх (вызов деструктора Destroy , вызов прослойки Free и вызов FreeAndNil ).

Статус мифа: confirmed .

Миф №15: если программа не освободит память, то в системе останется мусор и она замедлится

За эти 14 мифов мы уже столько раз запускали тестовые программы на выделение огромных количеств памяти (некоторые — даже больше размера установленной памяти в системе), но после закрытия программ система продолжала работать как ни в чём не бывало, что этот миф разрушен ещё до того, как я его озвучил.

Но особо дотошные могут его проверить самостоятельно: это будет домашнее задание для начинающих разрушителей легенд. Как бы вы его проверили?

Суть в том, что при закрытии программы, все ресурсы, которые были с ней ассоциированы (виртуальная память, физическая память, открытые файлы и т.п.), автоматически удаляются/закрываются/очищаются системой. Конечно, в системе есть глобальные ресурсы/объекты, не являющиеся частью состояния программы (и которые, таким образом, не меняются при закрытии программы), но виртуальная память к ним не относится.

Мне кажется, что миф пришёл из времён, когда адресное пространство было тождественно физической памяти: его ошибочно перенесли на современные системы, не разбираясь, как они работают.

AllocMem/EngAllocMem

newinfo2005, имхо может просто подсмотреть как общение с драйверами реализованно в коде, да хоть у тех же МС$ вроде легче разобратся в 50строках кода чем читать талмут в 50глав :biggrin

newinfo2005,
о нашел в закромах, и даже лучше чем ожидал

пример драйвера, и реализация интерфейса между драйвером и приложением :biggrin

Цитата (newinfo2005 @ 6.8.2008, 10:11 )
я так понимаю нельзя отослать некое событие например WM_COPYDATA приложению

Как вараиант — можешь в драйвере создать notification event, доступный из user-mode, в приложении его отлавливать (можно завести поток для этого), и когда событие наступило — уже считать данные из драйвера посредством ReadFile\DeviceIoControl

Что такое код allocmem

В. Возможно создавать динамически-изменяющиеся массивы в Delphi?

О. Да. Для начала вам необходимо создать тип массива, использующего самый большой размер, который вам, вероятно, может понадобиться. В действительности, при создании типа никакой памяти не распределяется. Вот когда вы создаете переменную этого типа, тогда компилятор пытается распределить для вас необходимую память. Вместо этого создайте переменную, являющуюся указателем на этот тип. Этим вы заставите компилятор распределить лишь четыре байта, необходимые для размещения указателя.

Прежде, чем вы сможете пользоваться массивом, вам необходимо распределить для него память. Используя AllocMem, вы можете точно управлять выделяемым размером памяти. Для того, чтобы определить необходимое количество байт, которые вы должны распределить, просто умножьте размер массива на размер отдельного элемента массива. Имейте в виду, что самый большой блок, который вы сможете распределить в любой момент в 16-битной среде равен 64Kб. Самый большой блок, который вы можете в любой момент распределить в 32-битной среде равен 4Гб. Для определения максимального числа элементов, которые вы можете иметь в вашем конкретном массиве (в 16-битной среде), разделите 65,520 на размер отдельного элемента. Например: 65520 div SizeOf(LongInt)

Пример объявления типа массива и указателя:

Затем, для распределения памяти под массив, вы могли бы использоваться следующую процедуру:

Не забывайте о том, что величина ArraySizeIWant должна быть меньше или равна MaxArraySize.

Вот процедура, которая с помощью цикла устанавливает величину каждого члена:

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

Помните также о том, что после использования массива всю распределенную память необходимо освободить. Вот пример того, как избавиться от этого массива:

Ниже приведен пример динамического массива:

allocmem

Эта функция не определена стандартом ANSI С.

Функция allocmem() выполняет функцию DOS 0x48, с помощью вызова которой выделяется блок памяти с выравниванием на границу параграфа. Она помещает адрес сегмента блока в пере­менную целого беззнакового типа, на которую указывает seg. Аргумент size определяет число параграфов, которые требуется выделить. (Параграф состоит из 16 байт.)

Если запрошенная память может быть выделена, то возвращается значение —1. Если свободной памяти мало, переменной, на которую указывает seg, не присваивается никакого значения, а возвращается размер наибольшего имеющегося в наличии блока памяти. Также переменная errno устанавливается равной значению ENOMEM (недостаточно памяти).

Что такое код MMI: убираем ошибку на устройстве с ОС Android

Многие владельцы гаджетов с ОС Android во время набора кода USSD видели уведомление о том, что возникли проблемы с подключением. В сегодняшней публикации рассмотрим, что такое код mmi, почему возникают такие ошибки. К слову, сам спецкод нужен для форматирования ussd запросов. Эти сообщения могут появляться независимо от используемого оператора. Вот только в первую очередь следует проверить, корректно ли введены данные. Весь перечень можно найти на сайте провайдера. Если все введено верно, но сбой все равно происходит, читайте инструкцию ниже.

Привлекаем режим полета

Что такое код mmi? Если проблема возникла в первый раз, то вероятнее всего, произошел сбой в работе сети. В таком случае воспользуйтесь простым методом. Для этого перейдите в настройки и тапните по значку самолета для активации режима полета, чтобы совсем отключить сеть.

Потом нужно немного подождать, примерно 30 секунд и можно его деактивировать. Дальше опять попробуйте ввести запрос, если спецкод mmi опять отменен, то придется пробовать другие варианты.

Нестабильная работа 3G или LTE

Такой сбой может появляться, если уровень сигнала плохой. Это будет видно по значку сети, если там тип меняется, то это ведет к сбоям в запросах. Для исправления необходимо выбрать один сетевой тип, их вы найдете в разделе настроек. Там нажмите значок «Еще», далее переходите к мобильным сетям, там и находится нужный пункт. Вы можете попробовать поочередно имеющиеся варианты, все зависит от покрытия вашего региона.

Что такое код mmi: проверка SIM карты

Эта проблема является самой распространенной, если вы ищите ответ на вопрос, что значит недействительный код mmi. Скорее всего, ваша симка уже давно используется или недавно она переставлялась с одного на другой девайс и была повреждена.

В таком случае попробуйте достать симкарту с аппарата и протереть ее спиртом или ластиком, это можно сделать и с телефонными контактами, если доступ к ним есть. При отрицательном результате нужно взять свой паспорт и пойти в салон оператора. Вам быстро сделают замену.

Ничего не помогает

Если не получилось исправить ситуацию, можете воспользоваться дополнительными советами. Они могут и не помочь, но все же они иногда выручали пользователей, так что пробуйте:

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

Что такое код mmi? Как вы поняли, это спецкод, использующийся для формирования запросов и обеспечивающий взаимодействие между сервисными программами оператора и абонентом. Если появилась ошибка, попробуйте воспользоваться вышеперечисленными инструкциями. Удачи!

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL