C/C++程序在执行时的内存分区

内存中的栈区和堆区

C程序在内存分为5个区:栈区、堆区、静态区、常量区、代码区。

1、栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等,其操作方式类似于数据结构中的栈(向低地址)。

2、堆区:就是通过new、malloc、realloc分配的内存块,由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收 。分配方式类似于数据结构中的链表。“内存泄漏”通常说的就是堆区。(向高地址增长)

3、全局区:存放全局变量和静态变量以及常量

        (1)静态区:存储全局变量和静态变量,程序结束后,由系统释放。

        (2)常量区

4、代码区:顾名思义,存放代码。

【精选】C语言的内存分配{静态内存&动态内存&堆栈}_c语言内存-CSDN博客

程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域

代码区:

存放 CPU 执行的机器指令

代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

全局区:

全局变量和静态变量存放在此.

全局区还包含了常量区, 字符串常量和其他常量也存放在此.

该区域的数据在程序结束后由操作系统释放.

在上面基础上,讨论变量有三种内存分配方式:

1、从静态存储区域分配。内存格局在程序编译的时候就已经明确好了,静态变量也都初始化好了,这块内存在程序的整个运行期间都存在,例如全局变量,static变量。

2、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放,栈内存分配运算内置于处理器的指令集中,效率很高,凡是分配的内存容量有限。例如 int a[10]。

3、从堆上分配。也称动态内存分配。程序在运行的时候用malloc或者new申请任意多少的内存,程序员自己负责在何时用free或者delete释放内存。动态内存的生存期有程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收他。否则运行的程序会出现内存泄漏,频繁的分配和释放不同大小的堆空间将会产生堆内碎块。

对应C/C++ 中变量的三种存储形式:
  • 静态存储:声明变量的时候前面添加static关键字;
  • 自动存储: 这个举个例子,在一个函数A里面定义了一个变量并初始化, int a =10 ; 这个就是自动存储,a仅当A()函数活动时存在。当成许控制权回到main()时,a使用的内存将自动被释放,
  • 动态存储:数据的声明周期不完全受程序或函数的生命时间控制 ,所以C++ 中有new 来分配空间,不过由于内存不会自动释放,所以使用完之后还需要使用delete 来释放内存。

————————————————
原文链接:https://blog.csdn.net/qq_35902025/article/details/127764973

栈、堆、静态存储区能申请的最大分配大小是多少呢?

1、栈区:默认大小为2M或1M,开的比较小;如果定义a[1024*1024];运行时就会报”段错误“,遇到要申请大的空间时,就需要动态申请。

        一种是:定义的局部数组太大   第二种是  递归深度太深。

2、静态存储区:全局变量,全局数组,静态数组(static)则是开在全局区(静态区)(static)。大小为2G,所以能够开的很大;

3、堆区:堆的最大可分配大小上限是由虚拟内存的最大值决定的。虚拟内存的最大值由

Min(内存容量和外存容量之和,计算机的地址位数能容纳的最大容量) 决定。

        总结一下,在默认情况下,栈只能得到1M大小的内存,全局静态储存可以得到2G,而在32位和64位下的堆则可以得到2G和无限内存(一般不会用到16T)。

(5条消息) 关于栈、堆、静态存储区最大可分配大小的探讨_lwnylslwnyls的专栏-CSDN博客

栈、堆(new/malloc)、静态区(static)的大小_堆栈分配4k,可申请静态数组大小为-CSDN博客

堆区/栈区详解

1、栈区

       因为栈主要是为一个线程配备(栈是线程独享的)为这个线程的函数调用服务的。用于存放返回地址,参数,临时变量而用,记录多层调用,是一个有明确的“后入先出”规则的内存 buffer。而且是供程序员“隐式”的使用,分配和回收都是“自动”的,程序员对栈内存的使用缺少自由和可控性, 栈这部分内存由专用的寄存器和指令来负责维护。其特点是栈的“复用率”很高,是连续的,且有 IN - OUT 次序的(对 ESP 的维护非常严格严谨,这个工作是编译器来负责,通常也不需要程序员去干预。计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。

      栈上对象的特点是尺寸小而且生命周期很短, 潜规则是不要创建尺寸极大的临时对象,调用深度也不希望过深。所以如果栈太大,则对内存使用的性价比会降低(栈的顶部方向的那部分属于栈所有但位于栈外的那部分内存是几乎不会被使用到的,距离栈顶越远的栈外内存的被使用的概率越低,距离栈底越近的内存则使用率越高,热点集中在靠近栈底的部分,而远端方向则是空白的,就好像地铁车厢的高度,不会因为一个姚明这样的存在就把它设计的很高,那样就属于浪费),而且也会减少可以同时创建的进程的数量。

      栈只能进行push和pop两种操作,无论是排序,寻址,还是修改,删除,插入都很难,时间代价太高了。所以 其实不是不能将栈变得很大,而是因为一旦变得很大,它在这个场景下就不是一个合理的数据结构了,应该选择其他的数据结构来解决。

2、堆区

       而堆内存的使用特点是不连续和无序的,分配和释放成本较高堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。 显然,堆的效率比栈要低得多。 ,

        因此,栈和堆的设计体现了“分工”原则。栈的主要作用的为函数调用服务,并承担了生命周期很短和(编译器)“自动”维护,尺寸很小的临时对象的存储责任。堆则负责存储生命周期由程序员自主维护,以及尺寸极大的(例如明显超过栈的尺寸)对象/数据。

栈区和堆区大小差异?

栈区:由图中其实可以知道,栈区是向低地址扩展的,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,大小在进程分配时是确定的,具体大小看编译器,操作系统。所需大小一般小于10M!太大没有意义,不符合栈是用来快速存取的目标。

堆区:堆区是向高地址扩展的,是不连续的内存区域(这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的是动态分配的),因为会手动进行分配,会大一些,大小不固定。

栈区和堆区效率差异?

至于堆区和栈区哪个更快,从两方面来考虑:

      (1)分配和释放,堆在分配和释放时都要调用函数(MALLOC,FREE),比如分配时会到堆空间去寻找足够大小的空间(因为多次分配释放后会造成空洞),这些都会花费一定的时间,具体可以看看MALLOC和FREE的源代码,他们做了很多额外的工作,而栈却不需要这些,由于栈先进后出的特性存取非常快,实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyorbelt)一样,StackPointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快。

   PS:我现在实习的一家量化交易公司,他们就绝不会允许使用malloc在堆区操作,因为堆的分配是离散的,对于他们高频连续的数据存取不是很方便。具体好像是内核旁路技术(搭配网卡)加上多线程(4线程)共享栈内存。什么同步不同步,有时间再继续填坑!

     (2)访问时间,访问堆的一个具体单元,需要两次访问内存,第一次得取得指针,第二次才是真正得数据,而栈只需访问一次。另外,堆的内容被操作系统交换到外存的概率比栈大,栈一般是不会被交换出去的。

栈区:由系统自动分配,速度较快。但程序员是无法介入分配。(只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。)

堆区:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。(首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的 delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中)

小结:其实从上面的知识我们可以看出,如果存放在堆中的数据如果不进行释放,很可能造成内存泄漏,因为并不一定能触发gc机制回收。所以对于堆中的内存使用,我们要记得用完释放。

堆栈缓存方式区别:
1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

参考:

堆、栈、栈区、堆区四者在数据结构和虚拟内存模型中的区别-CSDN博客

待了解:

【池化技术】内存池技术原理和C语言实现_ram 模拟heap c语言 内存池-CSDN博客 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是一个简单的实现内存分配与回收的 C 语言程序,包含了退出系统、显示分区、分配分区和回收分区等功能: ``` #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 1024 // 内存分区结构体 typedef struct { int start; // 分区起始地址 int size; // 分区大小 int status; // 分区状态(0-空闲,1-已分配) } MemBlock; MemBlock mem_blocks[MAX_SIZE]; // 内存分区数组 int block_num = 0; // 内存分区数量 // 初始化内存分区 void init_mem_blocks() { mem_blocks[0].start = 0; mem_blocks[0].size = MAX_SIZE; mem_blocks[0].status = 0; block_num = 1; } // 显示内存分区 void show_mem_blocks() { printf("Memory Blocks:\n"); printf("Start Address\tSize\tStatus\n"); for (int i = 0; i < block_num; i++) { printf("%d\t\t%d\t%d\n", mem_blocks[i].start, mem_blocks[i].size, mem_blocks[i].status); } } // 分配内存 int allocate_mem(int size) { int i; for (i = 0; i < block_num; i++) { if (mem_blocks[i].status == 0 && mem_blocks[i].size >= size) { // 找到空闲分区,分配内存 mem_blocks[i].status = 1; if (mem_blocks[i].size > size) { // 分裂分区 mem_blocks[block_num].start = mem_blocks[i].start + size; mem_blocks[block_num].size = mem_blocks[i].size - size; mem_blocks[block_num].status = 0; mem_blocks[i].size = size; block_num++; } return mem_blocks[i].start; } } return -1; // 没有可用分区 } // 释放内存 void free_mem(int start) { for (int i = 0; i < block_num; i++) { if (mem_blocks[i].start == start) { // 找到要释放的分区 mem_blocks[i].status = 0; if (i > 0 && mem_blocks[i-1].status == 0) { // 合并前面的空闲分区 mem_blocks[i-1].size += mem_blocks[i].size; for (int j = i; j < block_num - 1; j++) { mem_blocks[j] = mem_blocks[j+1]; } block_num--; i--; } if (i < block_num - 1 && mem_blocks[i+1].status == 0) { // 合并后面的空闲分区 mem_blocks[i].size += mem_blocks[i+1].size; for (int j = i+1; j < block_num - 1; j++) { mem_blocks[j] = mem_blocks[j+1]; } block_num--; } break; } } } int main() { int choice, size, start; init_mem_blocks(); // 初始化内存分区 do { printf("\n1. Show memory blocks\n"); printf("2. Allocate memory\n"); printf("3. Free memory\n"); printf("4. Exit\n"); printf("Enter your choice: "); scanf("%d", &choice); switch (choice) { case 1: show_mem_blocks(); break; case 2: printf("Enter the size to allocate: "); scanf("%d", &size); start = allocate_mem(size); if (start == -1) { printf("Failed to allocate memory.\n"); } else { printf("Memory allocated at address %d.\n", start); } break; case 3: printf("Enter the start address to free: "); scanf("%d", &start); free_mem(start); printf("Memory freed.\n"); break; case 4: printf("Exiting...\n"); break; default: printf("Invalid choice.\n"); } } while (choice != 4); return 0; } ``` 该程序使用内存分区结构体来表示内存分区,包含了分区起始地址、分区大小和分区状态等信息。在初始化内存分区,只有一个完整的空闲分区。在分配内存,先找到第一个满足要求的空闲分区,如果分配后剩余空间大于零,则将剩余部分分裂成一个新的空闲分区。在释放内存,如果相邻的空闲分区可以合并,则将它们合并成一个更大的空闲分区。 该程序可以通过菜单选择功能,包括显示内存分区、分配内存、释放内存和退出系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值