Я написал простой распределитель памяти, в основном создает цепочку выделенных блоков, они связаны друг с другом с помощью указателей, хранящихся в метаданных struct
до выделенной области.
Код работает отлично, и я могу выделить цепочку блоков размером sz
, которые затем освобождаются с помощью другой функции, написанной мной.
Проблема в том, что я заметил, используя команду vmmap
, что, по-видимому, память выделяется malloc
без явного запроса. Здесь вставка различных vmmap
, взятых в разных точках выполнения программы:
## before first alloc
# this has been taken before any allocation happens in the code.
REGION TYPE VIRTUAL
=========== =======
Kernel Alloc Once 4K
MALLOC 9388K see MALLOC ZONE table below
MALLOC (admin) 24K
STACK GUARD 56.0M
Stack 8192K
VM_ALLOCATE 8K <-- we start with 8K
__DATA 668K
__LINKEDIT 70.2M
__TEXT 5860K
shared memory 4K
=========== =======
TOTAL 149.8M
VIRTUAL ALLOCATION BYTES
MALLOC ZONE SIZE COUNT ALLOCATED % FULL
=========== ======= ========= ========= ======
DefaultMallocZone_0x10ad37000 9216K 363 27K 0%
=======================================================================
## after first alloc
# this is after the chain allocation.
REGION TYPE VIRTUAL
=========== =======
Kernel Alloc Once 4K
MALLOC 18.2M <-- why the hell does malloc() increase?
MALLOC (admin) 24K
STACK GUARD 56.0M
Stack 8192K
VM_ALLOCATE 8200K <-- this is expected, we allocate memory with vm_allocate()
__DATA 668K
__LINKEDIT 70.2M
__TEXT 5860K
shared memory 4K
=========== =======
TOTAL 166.8M
VIRTUAL ALLOCATION BYTES
MALLOC ZONE SIZE COUNT ALLOCATED % FULL
=========== ======= ========= ========= ======
DefaultMallocZone_0x10ad37000 18.0M 364 31K 0%
=======================================================================
## after chain release
REGION TYPE VIRTUAL
=========== =======
Kernel Alloc Once 4K
MALLOC 19.2M <-- malloc increases even more?!?
MALLOC (admin) 24K
STACK GUARD 56.0M
Stack 8192K
VM_ALLOCATE 8K <-- after chain release
__DATA 668K
__LINKEDIT 70.2M
__TEXT 5860K
shared memory 4K
=========== =======
TOTAL 159.8M <-- but why has total size increased?
VIRTUAL ALLOCATION BYTES
MALLOC ZONE SIZE COUNT ALLOCATED % FULL
=========== ======= ========= ========= ======
DefaultMallocZone_0x10ad37000 19.0M 364 31K 0%
Я на OS X, и я использую подпрограмму vm_allocate
для выделения памяти для моих блоков. Итак, как и ожидалось, раздел vm_allocate
в vmmap
показывает, что после освобождения цепочки память раздела возвращается к первоначальному размеру, то есть 8K. Я никогда не называю malloc
в своем коде.
Но, по-видимому, размер раздела malloc
увеличивается после выделения! И он никогда не освобождается. Общий размер после освобождения цепи на 10 МБ больше, чем до распределения цепи.
Кто-нибудь знает, почему это могло случиться? Я не думаю, что процедура vm_allocate
вызывает malloc
, это не имеет смысла. Заранее благодарим за помощь!
Тестирование без выделения кода
Я действительно прокомментировал весь код выделения, который выполнял моя программа, в основном оставил пустую main
функцию. Затем повторили три проверки vmmap
для моей программы. Результаты:
REGION TYPE VIRTUAL
=========== =======
Kernel Alloc Once 4K
MALLOC 9388K see MALLOC ZONE table below
MALLOC (admin) 24K
STACK GUARD 56.0M
Stack 8192K
VM_ALLOCATE 8K
__DATA 668K
__LINKEDIT 70.2M
__TEXT 5860K
shared memory 4K
=========== =======
TOTAL 149.8M
VIRTUAL ALLOCATION BYTES
MALLOC ZONE SIZE COUNT ALLOCATED % FULL
=========== ======= ========= ========= ======
DefaultMallocZone_0x105379000 9216K 363 27K 0%
=========================================================================
REGION TYPE VIRTUAL
=========== =======
Kernel Alloc Once 4K
MALLOC 10.2M see MALLOC ZONE table below
MALLOC (admin) 24K
STACK GUARD 56.0M
Stack 8192K
VM_ALLOCATE 8K
__DATA 668K
__LINKEDIT 70.2M
__TEXT 5860K
shared memory 4K
=========== =======
TOTAL 150.8M
VIRTUAL ALLOCATION BYTES
MALLOC ZONE SIZE COUNT ALLOCATED % FULL
=========== ======= ========= ========= ======
DefaultMallocZone_0x105379000 10.0M 363 27K 0%
=========================================================================
REGION TYPE VIRTUAL
=========== =======
Kernel Alloc Once 4K
MALLOC 11.2M see MALLOC ZONE table below
MALLOC (admin) 24K
STACK GUARD 56.0M
Stack 8192K
VM_ALLOCATE 8K
__DATA 668K
__LINKEDIT 70.2M
__TEXT 5860K
shared memory 4K
=========== =======
TOTAL 151.8M
VIRTUAL ALLOCATION BYTES
MALLOC ZONE SIZE COUNT ALLOCATED % FULL
=========== ======= ========= ========= ======
DefaultMallocZone_0x105379000 11.0M 363 27K 0%
vm_allocate
область теперь не увеличивается, как и ожидалось, никто не звонит vm_allocate
. Но, как вы видите, регион malloc
все еще увеличивается! Даже без моего кода. Он растет меньше, чем раньше, то есть 11 МБ вместо 18 МБ.
Это означает, что мой код оказывает прямое влияние на него, но что это может быть? Возможно, функции библиотеки вызываются malloc
? Я использую некоторые printf
в моем выделенном коде, и я знаю такие функции, как printf
call malloc
, но почему тогда память не освобождена?
EDIT - добавление кода
Извините, что не включил его раньше, и извините, если это полный беспорядок, я написал это примерно через два часа, и это моя первая попытка создать настраиваемый распределитель.
#include <mach/mach.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc/malloc.h>
#define BLOCK_NO 2048
typedef struct mem_block {
struct mem_block *next;
unsigned int sz;
unsigned int free:1;
} mem_block_t;
void *alloc_block(vm_size_t size)
{
void *block_addr=NULL;
mem_block_t metadata;
vm_address_t *start_addr=0;
vm_allocate(mach_task_self_, (vm_address_t*)&start_addr, size, 1);
block_addr=(start_addr);
metadata.next=(void*)NULL;
metadata.sz=(unsigned int)size;
metadata.free=0x0;
memcpy(block_addr, (mem_block_t*)&metadata, sizeof(mem_block_t));
return block_addr+sizeof(mem_block_t);
}
void dealloc_block(void *block_addr)
{
unsigned int sz=0;
vm_address_t start_addr = (vm_address_t)block_addr-sizeof(mem_block_t);
memcpy(&sz, (void*)start_addr+sizeof(mem_block_t*), sizeof(sz));
vm_deallocate(mach_task_self_, start_addr, sz);
}
void *alloc_block_chain(unsigned int blocks, vm_size_t size)
{
void *head=NULL, *old_block=NULL, *curr=NULL;
head = alloc_block(size);
old_block = head;
for (int i=0; i<blocks-1; i++) {
curr = alloc_block(size);
((mem_block_t*)old_block)->next=curr;
old_block=curr;
}
return head;
}
void dealloc_block_chain(void *block_addr_start)
{
int cnt=0;
void *curr=NULL, *old_block=NULL;
curr=block_addr_start;
while(1) {
if (old_block) {
dealloc_block(old_block);
malloc_printf("dealloc'd block #%d: %p\n", cnt, old_block);
cnt++;
}
if (!((mem_block_t*)curr)->next) {
dealloc_block(curr);
malloc_printf("dealloc'd final block: %p\n", curr);
break;
} else {
old_block = curr;
curr=((mem_block_t*)curr)->next;
}
}
}
int main(int argc, const char * argv[]) {
system("read -n 1 -s -p \"Press any key to continue...\";echo");
void *start = alloc_block_chain(BLOCK_NO, PAGE_SIZE);
void *curr=start;
for (int i=0; i<BLOCK_NO; i++) {
malloc_printf("block #%d: %p\n", i, curr);
curr = ((mem_block_t*)curr)->next;
}
system("read -n 1 -s -p \"Press any key to continue...\";echo");
dealloc_block_chain(start);
system("read -n 1 -s -p \"Press any key to continue...\";echo");
return 0;
}
Возможное решение
Вы можете видеть, что я использую malloc_printf
в коде. Я ранее называл printf
там. Функция malloc_printf
похожа на printf
, но избегает вызова malloc
.
Это, похоже, устраняет утечку! Я буду делать больше тестов позже, но для того, что я видел, проблема действительно может быть реализована в OS X printf
.