Распределение памяти в VC ++ - PullRequest
3 голосов
/ 15 мая 2009

Я работаю с VC ++ 2005 Я перегружен новыми и удаляю операторов. Все хорошо.

Мой вопрос связан с магическим добавлением VC ++ к выделению памяти.

Когда я использую вызов C ++:

data = new _T [size];

Возврат (например) из глобального распределения памяти - 071f2ea0, но данные установлены в 071f2ea4

При вызове перегруженного delete [] передается адрес 071f2ea0.

Еще одно замечание при использовании чего-то вроде:

data = new _T;

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

Я почти уверен, что Microsoft добавляет что-то во главе выделения памяти для использования в бухгалтерском учете. Мой вопрос: знает ли кто-нибудь о правилах, которые использует Microsoft.

Я хочу передать значение «data» в некоторые процедуры тестирования памяти, поэтому мне нужно вернуться к исходной ссылке на память из вызова глобального выделения.

Я мог бы предположить, что 4 байта являются индексом, но я хотел убедиться. Я мог бы легко быть флагом плюс смещение, или считать или и индексировать в какую-то другую таблицу, или просто выравнивать по строке кэша ЦП. Мне нужно узнать наверняка. Я не смог найти никаких ссылок, чтобы обрисовать детали.

Я также думаю, что на одном из моих других прогонов смещение было 6 байтов, а не 4

Ответы [ 4 ]

4 голосов
/ 15 мая 2009

Скорее всего, 4 байта содержат общее количество объектов в выделении, поэтому delete [] сможет перебрать все объекты в массиве, вызывая их деструктор.

Чтобы вернуть исходный адрес, вы можете сохранить таблицу поиска по адресу address / 16, в которой хранятся базовый адрес и длина. Это позволит вам найти оригинальное распределение. Однако вы должны убедиться, что ваше распределение + 4 не пересекает 16-байтовую границу.

EDIT: Я пошел дальше и написал тестовую программу, которая создает 50 объектов с помощью деструктора new и вызывает delete []. Деструктор просто вызывает printf, поэтому он не будет оптимизирован.

#include <stdio.h>

class MySimpleClass
{
    public:
    ~MySimpleClass() {printf("Hi\n");}
};

int main()
{
    MySimpleClass* arr = new MySimpleClass[50];
    delete [] arr;


    return 0;
}

Ниже приведена частичная разборка, очищенная для большей разборчивости. Как видите, VC ++ хранит количество массивов в начальных 4 байтах.

; Allocation
mov ecx, 36h ; Size of allocation
call    scratch!operator new
test    rax,rax ; Don't write 4 bytes if NULL.
je      scratch!main+0x25
mov     dword ptr [rax],32h ; Store 50 in first 4 bytes
add     rax,4 ; Increment pointer by 4

; Free
lea     rdi,[rax-4] ; Grab previous 4 bytes of allocation
mov     ebx,dword ptr [rdi] ; Store in loop counter
jmp     StartLoop ; Jump to beginning of loop
Loop:
lea     rcx,[scratch!`string' (00000000`ffe11170)] ; 1st param to printf
call    qword ptr [scratch!_imp_printf; Destructor
StartLoop:
sub     ebx,1 ; Decrement loop counter
jns     Loop ; Loop while not negative

Этот бухгалтерский учет отличается от бухгалтерского учета, который выполняют malloc или HeapAlloc. Эти распределители не заботятся об объектах и ​​массивах. Они видят только капли памяти с общим размером. VC ++ не может запрашивать у менеджера кучи общий размер выделения, потому что это будет означать, что менеджер кучи будет обязан выделять блок именно того размера, который вы запрашивали. Менеджер кучи не должен иметь этого ограничения - если вы запрашиваете 240 байтов для выделения 20 12-байтовых объектов, он может свободно возвращать 256-байтовый блок, который он сразу же получил.

1 голос
/ 15 мая 2009

Смещение 4 байта для количества элементов. Когда вызывается delete [], необходимо знать точное количество элементов, чтобы можно было вызывать деструкторы точно для необходимого количества объектов.

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

0 голосов
/ 15 мая 2009

Окончательный ответ:

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

Что я смог определить, так это то, что иногда C ++ добавляет / использует 4 дополнительных байта для подсчета выделяемых объектов. Суть в том, что эти 4 байта добавляются только в том случае, если выделяемые объекты требуют уничтожения.

Так дано:

void* _cdecl operator new(size_t size)
{
    void *ptr = malloc(size);
    return(ptr); 
}

для случая:

object * data = new object [size]

данные будут ptr плюс 4 байта (если объект требует деструктора)

в то время как:

char *data = new char [size]

данные будут равны ptr, потому что деструктор не требуется.

Опять меня не интересует отслеживание памяти malloc добавляет для управления памятью.

0 голосов
/ 15 мая 2009

Конечно, при распределении памяти требуется некоторая бухгалтерская информация, хранящаяся вместе с фактической памятью.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...