内存的分区
内存的分区及存放位置
一、内存分区介绍
程序中内存有以下分区:
栈区(stack)
临时创建的局部变量存放在栈区。
函数调用时,其入口参数存放在栈区。
函数返回时,其返回值存放在栈区。
const定义的局部变量存放在栈区。
堆区(heap)
堆区用于存放程序运行中被动态分布的内存段,可增可减。可以有malloc等函数实现动态分布内存。
有malloc函数分布的内存,必须用free进行内存释放,否则会造成内存泄漏。
全局区(静态区)(static)
内存在程序编译的时候就已经分配好了,这块内存在程序的整个运行期间都存在。它主要存放静态数据(局部static变量,全局static变量)、全局变量。当程序结束后,变量由系统释放 。常量区
存放数字 和 常量字符串。当程序结束后,由系统释放 。常量区的内容不可以被修改。
代码区
存放函数体的二进制代码。
1 | //main.cpp |
二、各分区特点
1.全局静态变量
定义:在全局变量之前加上关键字static,全局变量就被定义为一个全局静态变量。
说明:1.内存中的位置:静态存储区(静态存储区在整个程序运行期间都存在)
2.初始化:未经初始化的全局静态变量会被程序自动初始化为0(自动对象的值是任意的,除非它被显示初始化)
3.作用域:全局静态变量在声明它的文件之外是不可见。准确的讲从定义之处开始到文件结尾。
注意:
1.不会被其他文件所访问,修改。
2.其它文件中可以使用相同名字的变量,不会发生冲突。
3.虽然改变了全局变量的作用域,但存储位置仍是静态存储区
2.局部静态变量
定义:在局部变量之前加上关键字static,局部变量就被定义为一个局部静态变量。
说明:1.内存中的位置:静态存储区
2.初始化:未经初始化的局部静态变量会被程序自动初始化为0(自动对象的值是任意的,除非它被显示初始化)
3.作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域随之结束。
注意:当static用来修饰局部变量的时候,它就改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。但是局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对它进行访问。
3.堆与栈的区别
1.管理方式不同:对于栈来讲,是由编译器自动管理的,无需手动控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
2.空间大小不同:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小。在VC6下面,默认的栈空间是1M。
3.产生碎片不同:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,它们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出。在它弹出之前,在它上面的后进栈的内容已经被弹出。
4.生长方向不同:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
5.分配方式不同:堆都是动态分配的,没有静态分配的堆。栈有两种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配是由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现。
6.分配效率不同:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。
三、STM32中的内存存放
由前面的分析我们知道,代码区和常量区的内容是不允许被修改的,ROM(STM32就是Flash Memory)也是不允许被修改的,所以代码区和常量区的内容编译后存储在ROM中。
而栈、堆、静态变量、全局变量都是存放在RAM中。
四、Keil 的Build Output窗口
如上图,存在Code、RO-data、RW-data、ZI-data四个代码段大小。
其中Code就是代码占用大小,RO-data是只读常量、RW-data是已初始化的可读可写变量,ZI-data是未初始化的可读可写变量。
有些时候,我们需要知道RAM和ROM的使用情况如何,那么我们就可以使用下面的公式计算。
- RAM = RW-data + ZI-data
- ROM = Code + RO-data + RW-data
注意:上面讲的内存分区(代码段、bss段…)虽然讲的是内存分区,但实际上有些段,比如代码段在STM32这种嵌入式设备中是不会被放到RAM中而是放到ROM中,这是因为嵌入式的RAM实在太小了…但是在非嵌入式平台,以上的内存分区的各段全是放在RAM中的。
五、STM32的堆栈
1.启动文件里堆栈大小都是以words为单位的(1words = 4 bytes)
2 裸机时Keil编译器工程项目的启动文件中定义的栈为:系统栈+用户栈(2个合二为一,作为一个整体)。系统栈+用户栈:单片机系统(中断函数和中断嵌套)使用这个栈区进行入栈和出栈操作;用户程序(函数调用的形参、非静态局部变量以及函数调用信息)也使用这个栈区进行入栈和出栈操作。
3 在FreeRTOS下,stm32的栈被分为:系统栈和任务栈.
系统栈:只有单片机系统(中断函数和中断嵌套使用系统栈)在使用这个栈区(系统栈)进行入栈和出栈操作,这个栈不包含用户栈。c此时在启动文件里的那个stack只是系统栈。
而任务栈则是 由
FreeRTOSConfig.h
文件中的宏定义configTOTAL_HEAP_SIZE确定。任务栈本身就是一个全局数组,(FreeRTOS里的HEAP指的是栈,而并不是堆。。。)工程项目中创建的每一个任务的栈区(包括:每个任务的函数调用的形参、非静态局部变量以及函数调用信息)都是从 FreeRTOS内核的heap_4.c中定义HEAP区中进行申请和分配。
这个HEAP(用户栈区)来自于静态数组,所以它存在于数据段(具体为.bss段), 它和Keil编译器工程项目的启动文件中定义的堆不是一个概念。
FreeRTOS编程时,使用C语言的库函数malloc函数动态请求分配堆区,对于微控制器并不是线程安全的。使用库函数free函数释放空间,对于微控制器并不是线程安全的。为了线程安全,在使用FreeRTOS编程时,进行动态内存申请(Keil编译器工程项目的启动文件中配置的堆区)时,请使用FreeRTOS提供的pvPortMalloc和vPortFree函数。(还是从系统的堆区里申请,并不是FreeRTOS的HEAP)
参考地址: