143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|||||||||||
1 | |||||||||||
Реальная затрачиваемая память в динамике15.09.2016, 06:30. Показов 1065. Ответов 21
Метки нет (Все метки)
Динамическое выделение памяти.. допустим есть:
Ну и получается, что по крайней мере помимо 255*4 байт, выделяется ещё 4 байта, которые будут хранить размер. Но ведь и это по сути может быть не предел, может там есть ещё какие то необходимые поля. Может кто знает, или где почитать? (т.к. ссылки на другие сайты запрещены, можете выложить ключевую фразу для гугла или яндекса, чтоб сразу найти) Почему собственно данный вопрос возник.. Сижу разбираю статью о кастомных аллокаторах ("Альтернативные аллокаторы памяти" на хабре), где достаточно интересно представленны манипуляции с выделением памяти.. при использовании обычных аллокаторов new и calloc никогда и не задумываешся о том, что же на самом деле происходит "внутри", а ведь это достаточно интересно). Так вот, и подумалось.. а зачем собственно нам нужны все эти union'ы и варианты, где вечно встаёт вопрос о "неиспользуемой памяти" при выделении памяти для POD типов меньших размеров чем сами структуры. Не проще ли было бы использовать указатель смещения, в место того, чтоб полагаться на устройство ОС в отношении выделяемых объектов. Имею ввиду что то типа переменной uint8_t *mem в примере из статьи:
0
|
15.09.2016, 06:30 | |
Ответы с готовыми решениями:
21
PaintBox в динамике Задача по динамике Задача по динамике Задача по Динамике |
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
15.09.2016, 08:03 [ТС] | 3 |
В смысле VirtualAlloc или что вы имеете ввиду?
Как бы аллоктор new/delete (подозреваю что и calloc/malloc/realloc/free тоже) сделан на базе этой функции. Но это опять же уровень вверх, а что там под копотом VirtualAlloc?
0
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
17.09.2016, 09:50 [ТС] | 4 |
Тема что то так и не раскрыта.
(я вот честно не знаю что взять в пример.. calloc или new, так что берите что хотите.. я не особо хорошо понимаю в чём собственно различие, потому как кроме как exception'а и _msize по факту мало что бросается в глаза после прочтения с 50 всяких статей) Тогда начнём по порядку. (тема для windows 32разрядной и MSVC компилятора) 1) Оператор(аллокатор) new использует VirtualAlloc? 2) Читал что VirtualAlloc выделяет из общей ОЗУ блоки от 64 кбайт, то получается что при каждой операции выделении памяти через new где то в компиляторе(или где то ещё?) сидит некая оболочка(структура или класс?) которая должна поидее хранить: тип переменной, размер массива(если это массив или 1) и адрес. Так что же, получается что в реальности, для хранения любой переменной, созданой динамически - будет дополнительно израсходованно по мимо памяти для данных самой переменной (для начала только POD типы, т.е. от 1 до 8 байт) будет ещё 1 байт на тип, 4 байта на размер PTR, и собственно адрес ячейки в выделенной странице памяти, ах ну да, ещё и номер страницы.. так что ли? Или чуточку сложнее, и к примеру: на тип выделяется 2^4(4 бита, побитовыми операторами)(значение в 16 должно хватить) массивы выделяются отдельно, например как двух сторонний стэк.. т.е. при выделении обычной переменной(не массив), мы можем не тратить 4 байта на размер, а с другой стороны стэка тратим(для массива). адрес.. 4 байта? (или можно обойтись 2 байтами, т.к. у нас предел это одна выделенная страница) страница - фиг знает, походу надо 4гб разделить на 64 кб, т.е. если сойдётся, то 2 байта, если нет - то 4 байта) Добавлено через 8 минут П.С. Кстати, если мы заюзали 4 бита для типа, то можно ли оставшиеся 4 бита "прилепить" к адресу или странице? =)
0
|
Ушел с форума
|
||||||
17.09.2016, 10:10 | 5 | |||||
new/malloc - это "умная" обертка над функциями системы для выделения памяти, не обязательно VirtualAlloc.
Все немного не так. Во-первых, информация о типе существует только на этапе компиляции. Во-вторых, адрес хранить не нужно. Пример:
Тип int существует только на этапе компиляции. Адрес записан в p, за его хранение и очистку отвечает код программы. Потерял адрес - получил утечку.
0
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
17.09.2016, 12:51 [ТС] | 6 |
В смысле? У меня в ран-тайме переменные создаются. (ну понятное дело что указатель то инициализирован заранее)
Как может в ран-тайме не храниться инфа о размере переменной, если по значению указателя - берётся значение конкретного диапазона ячеек (1, 2, 4, 8 байт).. Запись как и чтение в конкретные ячейки происходят в ран-тайме, значит инфа хранится. И более того, если существует _msize() (правда не для ++new/delete, и он возвращяет размер выделенного PTR массива с учётом типа, то это лишь ещё раз указывает на это) В С/С++ нет встроенного функционала на авто-очистку, так что руками delete надо делать, так что нужно не просто адрес иметь, но и вызывать соответсвующую функцию (ну или оператор, который под копотом то всё равно имеет АПИ функцию ОС). Хотя по поводу очистки памяти.. Если использовать ручной контроль VirtualAlloc, то подозреваю, что если контролировать страницы, то в случае потери адреса.. можно и автоматом всё почистить. (не уверен, но я ещё покапаю эту тему)
0
|
Ушел с форума
|
|
17.09.2016, 15:51 | 7 |
Потому что компилятор генерирует соответствующий код: для char 1 байт, для short 2 и так далее.
В динамике эта информация не хранится (исключение разве что для размера массивов).
0
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
17.09.2016, 20:57 [ТС] | 8 |
Ну так и как же это происходит?
Как отличить что один указатель ссылается на 8 ячеек, другой на 16? И что конкретно вот эти ячейки следует читать и считать по правилам соответствующего типа? Т.е. если бы я функцию сделал SummFloat() и в него бы внёс правила для суммирования float..
0
|
Вездепух
11694 / 6373 / 1723
Регистрация: 18.10.2014
Сообщений: 16,057
|
|
17.09.2016, 21:09 | 9 |
Обсуждалось здесь и не раз. Бесстыже сошлюсь на себя
Можно ли обойти динамический массив не зная его размер? Можно ли обойти динамический массив не зная его размер?
0
|
Ушел с форума
|
||||||
17.09.2016, 21:34 | 10 | |||||
Когда мы пишем '*p = 123;', компилятор "знает", что p - это
указатель на int, поэтому генерирует примерно такой код:
Иначе были бы доступны такие фишки других языков, как рефлексия.
0
|
Вездепух
11694 / 6373 / 1723
Регистрация: 18.10.2014
Сообщений: 16,057
|
|
17.09.2016, 21:53 | 11 |
Информация о размере переменной не хранится,как ран-тайм значение.
Она явно или неявно жестко "зашивается" непосредственно в сгенерированные компилятором процессорные инструкции. Для работы с 1-байтными данными используются процессорные инструкции для работы с 1-байтными данными. Для работы с 4-байтными данными используются процессорные инструкции для работы с 4-байтными данными. И т.д. Если язык программирования поддерживает, например, 16-байтные целые числа, а машинная платформа не имеет процессорных инструкций для работы с целыми числами такой ширины, то компилятор будет генерировать некий более-менее нетривиальный набор инструкций для каждой операции с 16-байтными целыми. Опять же, этот набор инструкций будет жестко завязан на работу именно и только с 16-байтными целыми. Хранить и анализировать число 16 где-то в ран-тайме никто не будет.
0
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
18.09.2016, 04:07 [ТС] | 12 |
Вы говорите одно и тоже, не давая отсылок или объяснения как это происходит. Ну вот и толку от того что это всё говорите - никакого. "Компилятор знает" звучит как реклама пельмешек(или чего там я хз, мясо не ем) "папа может".
0
|
Вездепух
11694 / 6373 / 1723
Регистрация: 18.10.2014
Сообщений: 16,057
|
|
18.09.2016, 08:18 | 13 |
Процитированный вами кусок моего поста - это и есть исчерпывающее объяснение того, что происходит. Дальше можно лишь вывалить вам сюда систему команд какого-либо конкрентного процессора в качестве иллюстрации. Но я не вижу в этом смысла, тем более что вы сами можете запросто посмотреть. Пример странслированного кода вот тоже уже привели в сообщении #10.
Что касается "компилятор знает"... Я не понимаю, что модет быть непонятноего в этом утверждении. Вы явно объявили некий указатель p как указатель на int : int *p . Затем вы передали это объявление на вход компилятору. Почему вас удивляет тот факт, что комплятор теперь знает, на данные какого размера указывает указатель p ?Если для вас это ничего не объясняет, то надо делать шаг назад и начинать с каких еще более азбучных азов. Мы просто еще не знаем, насколько далеко назад необходимо отступить.
1
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
18.09.2016, 09:50 [ТС] | 14 |
А если я объявил указатель на void или union.
Потому что у меня вопрос о том памяти. В озу выделилось 4 байта для данных, а где информация о типе? С потолка он её не берёт.. Система команд отталкивается от типа. Где блин тип то хранится? Он же не на марсе хранится, а где то в памяти. и меня как то мало волнуют просто из того, что я и руками могу зайти на конкретный адрес и побитово вписать туда то что мне надо. А как я буду писать и считать предварительно эти биты - уже совершенно другая песня, думаю что вы и сами можете представить как. (по аналогии с тем как считаются "очень большие числа", которые не помещяются в 4 и 8 байтовые рамки) В 90-ые, когда такие темы были актуальны.
0
|
Ушел с форума
|
|||||||||||||||||||||
18.09.2016, 10:27 | 15 | ||||||||||||||||||||
Ее нет. Какие проблемы? У нас есть указатель int на этот адрес.
Если где-то в коде встречается строка с разыменованием этого указателя, компилятор генерирует вставку машинного кода с чтением sizeof (int) байт по этому адресу. Все. Зачем во время выполнения программы хранить информацию о типе? Это лишнее. Лишние траты по памяти и по эффективности. Напомню, что C++ - статически типизированный язык. Это значит, что информация о типах определяется на этапе компиляции и далее никогда не меняется. Отсюда логически вытекает, что хранение типа на этапе выполнения не нужно. С массивами, а также с классами, имеющими виртуальные функции, немного другая история, но это частности: в первом случае, как правило, где-то неподалеку от начала выделенного блока памяти хранится размер массива, во втором адрес на виртуальную таблицу, одну или несколько, для правильной работы полиморфных вызовов (через ссылку или указатель на базовый класс). Код для размышлений:
Кстати, ассемблерный листинг сгенерированного кода (VC++2015) тоже весьма показателен. Строка "int * p1 = (int *)Memory;" превращается в следующее:
никакой работы с типами нету. А вот откуда берется строковое значение для "typeid(p1).name()":
0
|
Модератор
8908 / 6677 / 918
Регистрация: 14.02.2011
Сообщений: 23,520
|
||||||
18.09.2016, 10:28 | 16 | |||||
а где хочет
это отдано на откуп компилятору, например VS перед выделенной памятью со смешением -4( вреде бы) плюс дебиговая версия создает еще и "подушки безопасности", для контролирования выхода за пределы массива int *i =new int(123); выделенный участок может выглядеть так ppp 1 123 ppp а может и по другому без дизасемблирования не разберешься вот попробуй такой хакерский трюк
1
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
|
18.09.2016, 11:36 [ТС] | 17 |
ПОТОМУ ЧТО ОН ОБРАЩЯЕТСЯ ТУДА. В ран тайм режиме записывается\читается переменная, что ж неясного-то.
Да не надо её менять. Да, на этапе компиляции заносятся данные куда то на потолок, что PTRqweINT = имеет тип int указателя, но как она определяется в ран тайме, что начиная с ХХХХFFFF байта в ОЗУ - именно PTRqweINT, а не PTRasdCHAR. Чисто логически невозможно не тратя доп. памяти не указывать что там именно INT PTR, а не собака на сене. Дальше по посту.. вроде интересно - но бесполезно полностью. ValeryS, ВОТ! Твоё предположение выглядит вполне логично, что где то там в памяти имеются ряд ячеек(перед каждым значением например, т.е. если указатель ссылается на XX, то данные будут 1 ячейкой ранее), которые отвечают за типы\размеры\что то там ещё.. П.С. значит если несколько ячеек выделены под инф. о переменной, то значит что помимо 4 байт например для данных, как минимум ещё 1 байт выделяется. (жаль что нет инфы сколько же будет в конечном итоге) Добавлено через 14 минут Добавлю.. Вот.. это говорит о том, что инструкция DWORD ассемблерного листинга вызывается потому что "вшитые" данные относятся к определению переменной PTRqweINT. Т.е. в начале работы программы выделяется память, куда записываются данные о типах объявленных переменных, в данном случае - это указатель на INT. Значит что данные переменной и тип переменной могут(а скорее всего так и есть) находиться совершенно в разных местах, и место для данных переменной которое выделено с помощью VirtualAlloc совершенно не имеет зависимости с местом, где хранится тип(type_id) этой PTRqweINT.
0
|
Модератор
8908 / 6677 / 918
Регистрация: 14.02.2011
Сообщений: 23,520
|
||||||
18.09.2016, 11:42 | 18 | |||||
а вот это нет
может и без типа вот кусок дизасемблированой проги
чтение/запись/ сравнения а что там лежит? структура? класс? массив? а процессору по барабану он числа записывает или считывает все это сделал компилятор причем для ARM процессора код будет совершенно другим чтобы понять логику человеку приходится вручную разгребать почитай книгу "Искусство дизасемблирования" К. Касперски, там эти вопросы рассматриваются
0
|
Ушел с форума
|
|
18.09.2016, 19:49 | 19 |
Вопрос прозвучал так, цитирую дословно:
На него тебе ответили так: По-моему, ответы и приведенные к ним примеры были исчерпывающие. В рантайме читается только значение, но не тип. Тип/размер уже определен на этапе компиляции. Ну я не знаю, как это объяснить "на пальцах". Я пас. Никак не определяется. Да не нужна эта информация в рантайме. Не-нуж-на. То, что там, к примеру, int, а не bool, компилятор использует для генерации кода. В первом случае будет mov dword ptr, а во втором mov byte ptr. Ничего там не выделяется. Только служебная информация для аллокатора памяти. Нет, не так. Компилятор видит, что там int, и генерирует код с DWORD. Это все происходит на этапе компиляции, повторю. В рантайме уже никаких проверок типов нету, потому что это неэффективно. То же самое и с const, кстати говоря. Представь себе какую-нибудь числодробилку: расчет хэшей, кодирование медиа-потока, шифрование и т.п. Если бы там на обращение к каждому DWORD-у была проверка типа, то мы бы видео ни в одном плеере, написанном на C++, не могли бы смотреть. Ибо тормоза бы просто зашкаливали. Нет, ничего такого не делается. Можешь взять в руки дизассемблер и убедиться самостоятельно. typeid(x).name(), пример которого я привел, определяет тип тоже статически (исключение - классы с виртуальными таблицами и т.п.), т.е. по информации, взятой на этапе компиляции, вставляется соответствущая текстовая строка. Там даже ветвлений нет. Кстати, в стандарте языка вроде есть пункт, который оговаривает такие моменты (поиск по ключевым словам "static type"). Ну и какой же тип у данных, адрес которых вернула функция VirtualAlloc? Я же тебе привел пример, где память и адрес ее начала один и тот же, а типы разные.
0
|
143 / 122 / 21
Регистрация: 13.11.2012
Сообщений: 1,564
|
||||||
18.09.2016, 20:28 [ТС] | 20 | |||||
Ну сделаю я так (если это возможно):
Если мы вшиваем карман, то он будет реально существовать, вы же мне говорите что то типа: мы его вшили, но его не существует. Это же вздор! И вней "вшит" тип? Всё равно где. Вшито было на этапе компиляции, но извлечение данных идёт в ран-тайме. Потому что я в ран-тайме создаю 100500-ый экземпляр этой PTRqweINT. Это Вы откуда взяли?
0
|
18.09.2016, 20:28 | |
18.09.2016, 20:28 | |
Помогаю со студенческими работами здесь
20
PaintBox в динамике 2 Отчет по динамике Задача по динамике Обьект в динамике Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |