C++ 内存管理

1. C++内存管理

C/C++编译的程序占用的内存分区

  • 栈区(stack):由编译器自动分配释放 ,存放函数的参数名,局部变量的名等。其操作方式类似于数据结构中的栈。
  • 堆区(heap):由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
  • 全局/静态存储区 :全局变量和局部静态变量的存储是放在一块的(在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分)。程序结束后由系统释放。
  • 常量存储区 :常量字符串就是放在这里的,正常情况不允许修改,程序结束后由系统释放 。
  • 程序代码区 :放函数体的二进制代码。

在C++中,内存分为:栈、堆、自由存储区、全局/静态存储区、常量存储区。

  • 栈,在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束是这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率高,分配的内存容量有限。
  • 堆,就是那些由malloc等分配的内存块,用free来释放内存。
  • 自由存储区,那些由new分配的内存块,由应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
  • 全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
  • 常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。

 

堆和自由存储区的区别与联系

从技术上来说,堆(heap)是C语言和操作系统的术语。堆是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free可把内存交还。而自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区就区别于堆了。我们所需要记住的就是:

  • 堆是C语言和操作系统的术语、是操作系统维护的一块内存,而自由存储是C++中通过new与delete动态分配和释放对象的抽象概念。堆与自由存储区并不等价。
  • new所申请的内存区域在C++中称为自由存储区。藉由堆实现的自由存储,可以说new所申请的内存区域在堆上。

1.1.1.3 堆和栈的区别

  • 管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
  • 空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,默认的栈空间大小是1M(VS2017 项目-属性-链接器-系统可以修改)。
  • 碎片问题:对于堆来讲,频繁的malloc/free势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。栈是先进后出的队列,以至于永远都不可能有一个内存块从栈中间弹出。
  • 分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
  • 分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

 

 

2. new/delete和malloc/free的区别与联系:

  • new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。
  • 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
  • new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void * ,需要通过强制类型转换将void*指针转换成我们需要的类型。
  • new内存分配失败时,会抛出bac_alloc异常。malloc分配内存失败时返回NULL。
  • new会先调用operator new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存(通常底层使用free实现)。
  •  malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
  • C++允许重载new/delete操作符,特别的,布局new的就不需要为对象分配内存,而是指定了一个地址作为内存起始区域,new在这段内存上为对象调用构造函数完成初始化工作,并返回此地址。而malloc不允许重载。
  • new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。自由存储区不等于堆,如上所述,布局new就可以不位于堆中。

注意:

堆是一个实际的区域,而自由存储区是一个更上层的概念。通常new确实是在堆上申请内存,但是程序员可以自己重载new操作符,使用其他内存来实现自由存储(这并不常见)。另外,c++ primer plus这本书上有提到布局new,可以为对象在栈上分配内存。总的来说,自由存储区是new申请的区间的概念。

 

 

既然new/delete的功能覆盖了malloc/free,为什么C++还要保留malloc/free?因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。

new/delete、malloc/free底层实现原理:new/delete的底层实现是调用malloc/free函数实现的,而malloc/free的底层实现也不是直接操作内存而是调用系统API实现的。

new/delete的两种分配方式原理图如下:

 注意,针对上图最末尾所述的“new[]/delete[]时会多开辟4字节用于存储对象个数”,作如下说明:

对于内置类型:

  • new []不会在首地址前4个字节定义数组长度。
  • delete 和 delete[]是一样的执行效果,都会删除整个数组,要删除的长度从new时即可知道。

对于自定义类型:

  • new []会在首地址前4个字节定义数组长度。
  • 当delete[]时,会根据前4个字节所定义的长度来执行析构函数删除整个数组。
  • 如果只是delete数组首地址,只会删除第一个对象的值。 

1. 参考资料:

1. C++内存管理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值