C/C++ calloc() 函数的一些内幕
文章目录
看到一篇讲到关于 C/C++ calloc() 一些内幕的好文,本文简单记录分享一下。
1、calloc()
比较广为人知的部分,malloc()
和 calloc()
都是分配内存的函数,malloc()
返回未初始化的内存;而 calloc()
相当于调用了 malloc 然后调用 memset 来给分配的内存填充 0。
|
|
The calloc() function contiguously allocates enough space for count objects that are size bytes of memory each and returns a pointer to the allocated memory. The allocated memory is filled with bytes of value zero.
既然这样,为什么不直接用 malloc + memset 来代替 calloc 呢?
1.1、差异
calloc(count, size)
是通过 count * size 相乘结果来决定分配多少内存的,但它同时还会检查结果是否溢出,溢出则会返回 NULL,整数溢出可能会导致实际上分配的内存是很小的而没发现到;而 malloc 不会这样检查,因为它接收的是单个参数,一个用户认为设置没问题的参数。
上面只是一个安全性相关的差异,而两者还有性能上的差异。在文中测量 1GB 分配/释放的基准测试中,calloc + free
比 malloc + memset + free
快了 100 倍以上。(注:只是因为需要用到 memset,不然只使用 malloc 会更快,并非所有场景都需要填充 0 初始化内存,比如读文件到内存中,可以直接覆盖到。)
这是因为 calloc 作弊了,对于小内存分配(glibc 的区分是 128kB),calloc 的调用与使用 malloc + memset 一样,速度基本一致;但对于大内存分配时,差异出现了。
大内存的分配,一般需要内存分配器去向操作系统去申请分配,当操作系统将内存分配给进程时,它总是先将其清零,因为否则的话我们就可以看到其他进程使用到的内存遗留痕迹。所以对于这种已经清零的内存,没有必要再调用 memset。这个作为开发者无法直接判断,但 calloc 实现在标准库内存分配器当中,因此它可以判断返回的内存是否是来自操作系统的新内存。
除了新内存不做重复的初始化内存操作,calloc 分配的内存是写时复制的,当第一次写入时才会对应到 RAM 真实内存页。而 malloc 由于使用了 memset,就需要立刻付出内存分配映射的代价。
注:由于这个优化并未在官方函数标准文档里说明,所以还需要区分,是依平台而定的。
参考
文章作者 calssion
上次更新 2023-08-14