new和malloc的区别、ptmalloc

一、new和malloc的区别

1、new/delete是C++的运算符/关键字,malloc与free是c++/c语言的标准函数

void* malloc(size_t);
void free(void*);
void *operator new (size_t);
void operator delete (void *);
void *operator new[] (size_t);
void operator delete[] (void *);

2、new开辟的内存是自由存储区,malloc堆上开辟

自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。

那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operator new 的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operator new在哪里为对象分配内存。

3、new 还可以做初始化,malloc单纯开辟内存

int* buffer = new int(); // 分配的一个int初始化为0
int* buffer = new int(0); // 分配的一个int初始化为0
int* buffer = new int[512](); // 分配的512个int都初始化为0
int* buffer = new int(5); // 分配的一个int初始化为5

但是无法将分配的所有元素同时初始化为非0值,以下代码是不合法的:

int* buffer = new int[512](0); // 语法错误!!!
int* buffer = new int[512](5); // 语法错误!!!

C++11
C++11中增加了初始化列表功能,所以也可以使用以下的方式进行初始化:

int* buffer = new int{}; // 初始化为0
int* buffer = new int{0}; // 初始化为0
int* buffer = new int[512]{}; // 512个int都初始化为0
int* buffer = new int{5}; // 初始化为5

与上面不同的是,如下写法是合法的:

int* buffer = new int[512]{5}; // 第一个int初始化为5,其余初始化为0

但是其结果与我们设想的不一样,它并不是将分配的512个int都初始化为5,而仅仅是将第一个int初始化为5,其余的511个仍然初始化为0

而且正如初始化列表中“列表”两字所指出的,我们实际上可以用一个列表来初始化分配的内存:

int* buffer = new int[512]{1, 2, 3, 4}; // 前4个int分别初始化为1、2、3、4,其余int初始化为0
 

operator new 开辟内存 
constructor 内存初始化

 

4、返回类型安全性

new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。

5、内存分配失败时的返回值

new内存分配失败时,会抛出bad_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL(要用if来判断 是否开辟成功)

try
{
    int *a = new int();
}
catch (bad_alloc)//内存动态分配错误
{
    ...
}

6、在使用上的区别

 new 返回指定类型的指针,并且可以自动计算所需要大小。而 malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。并且C++提供了new[]与delete[]来专门处理数组类型

7、new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构,而malloc则不会

8、是否可以被重载

opeartor new /operator delete可以被重载。标准库是定义了operator new函数和operator delete函数的8个重载版本。而malloc/free并不允许重载。

9、new可以调用malloc ,但是malloc不能调用new
operator new可以重载 在重载的时候可以调用malloc  malloc是系统版本 兼容版本  向前兼容

10、能够直观地重新分配内存


使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来的内存区域。

new没有这样直观的配套设施来扩充内存。

总结

图来自: 动态内存分配、malloc与new的区别

有了malloc/free为什么还要new/delete?

  1. new运算不需要进行强制类型转换,使用简单方便;
  2. new运算是通过调用构造函数初始化动态创建的对象,执行效率更高;
  3. 使用new能够进行异常处理,使用更安全

 

二、ptmalloc

ptmalloc 实现了 malloc(),free()以及一组其它的函数. 以提供动态内存管理的支持。

malloc实现原理:

因为brk、sbrk、mmap都属于系统调用,若每次申请内存,都调用这三个,那么每次都会产生系统调用,影响性能;其次,这样申请的内存容易产生碎片,因为堆是从低地址到高地址,如果高地址的内存没有被释放,低地址的内存就不能被回收。 
   
  所以malloc采用的是内存池的管理方式(ptmalloc),Ptmalloc 采用边界标记法将内存划分成很多块,从而对内存的分配与回收进行管理。为了内存分配函数malloc的高效性,ptmalloc会预先向操作系统申请一块内存供用户使用,当我们申请和释放内存的时候,ptmalloc会将这些内存管理起来,并通过一些策略来判断是否将其回收给操作系统。这样做的最大好处就是,使用户申请和释放内存的时候更加高效,避免产生过多的内存碎片。
 

在没有用malloc 分配内存之前,堆区没有内存。

Linux 进程的默认地址空间, 对 heap 的操作, 操作系统提供了brk()系统调用,设置了Heap的上边界, 对 mmap 映射区域的操作,操作系 统 供了 mmap()和 munmap()函数。

因为系统调用的代价很高,不可能每次申请内存都从内核分配空间,尤其是对于小内存分配。 而且因为mmap的区域容易被munmap释放,所以一般大内存采用mmap(),小内存使用brk()。

在开辟<=128K时,brk指针会向后走,第一次申请的字节若不超过128k,系统会为其分配128k+4K的空间,如果多次malloc申请空间,每申请一次就需要修改一次mm_struct中brk的位置,就要执行一次系统调用,这样系统消耗会特别大,而多出来的4K的空间存放了分配信息,当free()时会用到。开辟的内存在用户空间的库上,当free时,这块空间归还给库,当进程结束时,归还系统。

在开辟〉128K时,通过mmap映射在堆和栈的中间映射一段内存,free时直接归还给系统。

不管内存是在哪里被分配的,用什么方法分配,用户请求分配的空间在ptmalloc中都使用一个chunk来表示。  用户调用free()函数释放掉的内存也并不是立即就归还给操作系统,相反,它们也会被表示为1个chunk, ptmalloc使用特定的数据结构来管理这些空闲的chunk。

malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。
 

参考: 进程地址空间

malloc的底层实现(ptmalloc)

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值