进程内存分布
各分区具体存放对应的数据可见:浅谈C语言内存
注意:const修饰的全局变量也储存在常量区,const修饰的局部变量依然在栈上
可参考我画的C/C++程序内存分布图结合Linux进程地址空间分布进行理解(欢迎指正)
为什么要分区?
意义:这种内存分区的目的是为了方便管理内存和提高内存利用率。堆区可以动态地分配内存,而栈区可以快速地分配和释放内存。全局/静态存储区和文字节码区则分别用于存储长期存在的数据和程序代码。总之不同区域存放的数据赋予不同的生命周期, 给我们更大的灵活编程
malloc
malloc 申请内存的时候,会有两种方式向操作系统申请堆内存。
● 方式一:通过 brk() 系统调用从堆分配内存
● 方式二:通过 mmap() 系统调用在文件映射区域分配内存;
感兴趣可详细阅读:实现malloc
Linux 内存管理 详解
malloc/calloc/relloc、free函数详解
剖析C动态内存管理 (malloc,calloc,realloc,柔性数组)
void* malloc(void* ptr)
: 向内存申请一块连续空间(字节为单位),并返回指向块起始的指针;新分配的内存未初始化为随机值。
void* free(void* ptr)
:释放动态开辟的内存空间。(取消关系不会清空内存内容)
void* calloc(size_t num, size_t size);
:为num个大小为size的元素开辟一块空间,并把空间每个字节初始化为0
void* realloc(void* ptr, size_t size)
:调整指向的动态内存空间到size大小
共性:
○ malloc, calloc, realloc
都是从堆上动态申请空间的函数,都在头文件 stdlib.h
中;
○ 使用都需要free
手动释放;
○ 返回值类型都为 void*
,根据使用情况而定所以使用一般需要强转类型。
○ 使用时都需要判断返回值是否为NULL
,来确保空间申请成功。
new和malloc
malloc 是C语言中动态申请内存空间的函数, 使用需要包含其头文件<stdlib.h>/<malloc.h> 在C++中兼容C语言内存管理的方式,但提供了更方便使用的 new和delete
共同点:
都是从堆上动态申请空间,并且都需要程序员手动进行释放
不同点:
- new 是操作符可以重载,而malloc/free是函数
- malloc 申请空间时需要计算空间大小作为参数传递,成功返回值void* ,一般都需要强转; new 只需要在new后面给出类型即可,返回值是对应数据类型的指针,不用强转
- malloc 申请空间时不会进行初始化, 而 new 可以初始化
- 申请自定义类型时, new会调用构造函数完成对象的初始化,delete在释放空间时会调用析构函数完成释放; 而malloc/free 只会申请和释放空间
- malloc 申请失败时返回的是NULL, 申请后需要判空检测是否成功,;new 不需要,new 申请失败会抛出异常,需要捕获异常
内存泄漏
内存泄漏是因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。 其实就是内存在程序运行中动态申请的内存空间由于某种原因程序未释放或无法释放。
C/C++程序中一般我们关心两种方面的内存泄漏:
- 堆内存泄漏(Heap leak)
堆内存指的是程序中通过malloc/calloc/realloc/new等从堆中动态申请的的内存,使用后没有通过调用 free或者delete 释放掉,导致以后这部分空间将无法再被申请使用,就会产生Heap Leak。 - 系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
如何一次在堆上申请4G的内存?
因为32位的环境下虚拟地址空间的大小只有4g,而光内核空间就需要1g,所以不可能申请得到,只有在64位的环境下才可以实现,只需要把执行环境改为64x即可
int main()
{
void* p = new char[0xfffffffful];
cout << "new:" << p << endl;
return 0;
}