前言
大家好, 我jiantaoyab,这篇文章给大家介绍mallo和free面试中常问到的问题。
malloc是如何分配内存的?
如果用户分配的内存小于128KB,则通过brk()申请内存
如果用户分配的内存大于128KB,则通过mmap()申请内存
简化进程虚拟地址空间图
每一个进程中都有一个struct task_struct{}里面有一个内存指针指向进程的虚拟地址空间,在栈和堆区中间未分配区域称为文件映射区。
通过代码验证
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{void* p = malloc(1024);printf("起始地址 : %p\n", p);while(1) sleep(1);return 0;
}
程序运行起来,查看到进程号,进入该进程号的目录下面
用cat命令查看maps文件
可以看到,申请出来的空间是在heap的上面一点点,是调用brk()申请出的内存空间。
那我们将申请的空间改大一点,再来验证一下
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{void* p = malloc(128 * 1024);printf("起始地址 : %p\n", p);while(1) sleep(1);return 0;
}
可以看到在64位的机器下,这个地址非常的大,刚刚能看到的heap也不见了,说明是通过mmap()申请内存。
malloc(1)会分配多大内存?
我们将用代码来验证,还是用上面的代码
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{void* p = malloc(1);printf("起始地址 : %p\n", p);while(1) sleep(1);return 0;
}
申请出来空间的范围是00ff8000-01019000 = (2 1000)16 = (135168)10 / 1024 = 132kb,所以malloc(1)会申请出132kb的内存。
free释放内存,会归还给操作系统吗?
继续通过代码验证
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{void* p = malloc(1024);printf("起始地址 : %p\n", p);getchar();free(p);printf("free()!\n");while(1) sleep(1);return 0;
}
运行起来,分配了空间
调用free,可以看到空间没有还给操作系统,这里是交给malloc去管理了
把代码改大一点
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{void* p = malloc(1024 * 128);printf("起始地址 : %p\n", p);getchar();free(p);printf("free()!\n");while(1) sleep(1);return 0;
}
可以看到,已经将空间归还给操作系统了。
总结
- malloc申请的空间小于128k,释放内存,不会还给操作系统由malloc内部管理起来
- malloc申请的空间大于等于128k,释放内存,还给操作系统
free函数如何知道释放多少内存?
查看glibc版本,通过https://ftp.gnu.org/gnu/glibc/下载
下载下来后在malloc.c下面,能看到
struct malloc_chunk {INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */struct malloc_chunk* fd; /* double links -- used only if free. */struct malloc_chunk* bk;/* Only used for large blocks: pointer to next larger size. */struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */struct malloc_chunk* bk_nextsize;
};
INTERNAL_SIZE_T : size_t 64位机器下 8字节
prev_size: 上一个chunk的大小size:当前块的大小,包括头部
当空闲的时候
fd:指向同一个bin中的下一个free chunk
bd:指向同一个bin中的前一个free chunk
free源代码
#define chunk2mem(p) ((void*)((char*)(p) + 2*SIZE_SZ))
//通过偏移得到起始块的地址
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ)) //判断一个块是否使用
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1/* extract inuse bit of previous chunk */
#define prev_inuse(p) ((p)->size & PREV_INUSE)__libc_free(void* mem)
{mstate ar_ptr;mchunkptr p; /* chunk corresponding to mem *///异常处理void (*hook) (__malloc_ptr_t, const __malloc_ptr_t)= force_reg (__free_hook);if (__builtin_expect (hook != NULL, 0)) {(*hook)(mem, RETURN_ADDRESS (0));return;}if (mem == 0) /* free(0) has no effect */return;p = mem2chunk(mem);if (chunk_is_mmapped(p)) /* release mmapped memory. */{/* see if the dynamic brk/mmap threshold needs adjusting */if (!mp_.no_dyn_threshold&& p->size > mp_.mmap_threshold&& p->size <= DEFAULT_MMAP_THRESHOLD_MAX){mp_.mmap_threshold = chunksize (p);mp_.trim_threshold = 2 * mp_.mmap_threshold;}munmap_chunk(p);return;}ar_ptr = arena_for_chunk(p);_int_free(ar_ptr, p, 0);
}
free函数是通过struct malloc_chuck结构体中的size知道需要释放多少内存空间的。
free空指针会崩吗?
并不会
if (mem == 0) /* free(0) has no effect */return;