---
title: "内存模型"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/cpp-memory-model/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/02-编程语言与运行时/C++/内存模型.md"
content_hash: 9f2baa70a3f6eed8b2770d61c302f5083993bdb6efbc288641275490378a199a
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# 内存模型

﻿# 内存模型

> 创建时间：2020/3/10 12:51

* 内存占用
  * 三种内存分配方式
    * 从静态存储区分配
    * 在栈上创建
    * 从堆上分配（动态内存分配）
  * 关于堆区和栈区
    * 堆和栈的区别
  * 内存对齐（memory alignment）
  * 内存的使用简介
  * 函数调用的过程——函数栈
  * 经典的堆栈的讲解
  * new和malloc的区别
  * 具体的内存分配问题
  * Ref

## 内存占用

栈区（stack）：由编译器自动分配与释放，存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。
堆区（heap）：一般由程序员自动分配，如果程序员没有释放，程序结束时可能有OS回收。其分配类似于链表。
全局/静态存储区 （static .bss段和.data段）：存放全局变量、静态数据、常量。程序结束后由系统释放。全局区分为已初始化全局区（data）和未初始化全局区（bss）。
常量区（.rodata段）：存放常量字符串，程序结束后有系统释放。
代码区（.text段）：存放函数体（类成员函数和全局区）的二进制代码。

## 三种内存分配方式

#### 从静态存储区分配

内存在程序编译的时候已经分配好，这块内存在程序的整个运行期间都存在。例如全局变量，static变量。

#### 在栈上创建

在执行函数时，函数内局部变量的存储单元可以在栈上创建，函数执行结束时，这些内存单元会自动被释放。
栈内存分配运算内置于处理器的指令集，效率高，但是分配的内存容量有限。

#### 从堆上分配（动态内存分配）

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

![assets/0009 - 内存模型__resource-001-ff6393a054f1.png](/media/5f75f9332608bd8d937d.png)

## 关于堆区和栈区

栈区（stack）用于存放局部变量、返回地址、函数的实参等，由编译器自动分配释放。栈的功能主要是函数调用、局部变量申请、函数参数传递所使用的空间，是为函数调用的实现提供一些保存、恢复操作。一般情况下栈空间都特别小。
在windows下，栈地址是向低地址扩展的数据结构，是一块连续的内存区域。栈顶的地址和栈的最大容量是系统预先规定好的，在windows下，栈的大小是2MB，而申请堆空间的大小一般小于2GB
栈存放的数据占空间比较小，而且栈内存不由程序员来管理，存放在栈里面的局部变量随着函数调用结束就自动释放了。

堆区（heap）是一个程序运行动态分配的内存区域。一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收。 程序员通过new等方式创建的对象，一般会在堆区中分配内存。

我们平常说的GC，指的是对堆内存的回收。对内存的优化一般也指的是堆内存。

### 堆和栈的区别

**管理方式** 不同： **栈** 是由 **编译器自动** 申请和释放空间， **堆** 是需要 **程序员手动** 申请和释放；
**空间** 大小不同： **栈** 的空间是有限的，在32位平台下，VC6下默认为 **1M** ， **堆** 最大可以到 **4G** ；
能否产生 **碎片** ： **栈** 和数据结构中的栈原理相同，在弹出一个元素之前，上一个已经弹出了， **不会** 产生碎片，如果不停地调用 **malloc、free堆** 造成内存 **碎片很多** ；
**生长方向** 不同： **堆** 生长方向是 **向上** 的，也就是向着内存地址增加的方向， **栈** 刚好相反，向着内存 **减小的方向** 生长。
**分配方式** 不同： **堆** 都是 **动态分配** 的，没有静态分配的堆。 **栈** 有 **静态** 分配和 **动态** 分配。 **静态** 分配是 **编译器完成** 的，比如局部变量的分配。 **动态分配** 由 **malloc 函数进行分配** ，但是栈的动态分配和堆是不同的，它的 **动态分配是由编译器进行释放** ，无需我们手工实现。
**分配效率** 不同： **栈** 的效率比堆 **高** 很多。栈是机器系统提供的数据结构，计算机在底层提供栈的支持，分配专门的寄存器来存放栈的地址，压栈出栈都有相应的指令，因此比较快。 **堆** 是由库函数提供的，机制很复杂，库函数会按照一定的算法进行搜索内存，因此比较慢。

栈区（stack）用于存放局部变量、返回地址、函数的实参等，由编译器自动分配释放。栈的功能主要是函数调用、局部变量申请、函数参数传递所使用的空间，是为函数调用的实现提供一些保存、恢复操作。一般情况下栈空间都特别小。
在windows下，栈地址是向低地址扩展的数据结构，是一块连续的内存区域。栈顶的地址和栈的最大容量是系统预先规定好的，在windows下，栈的大小是2MB，而申请堆空间的大小一般小于2GB
栈存放的数据占空间比较小，而且栈内存不由程序员来管理，存放在栈里面的局部变量随着函数调用结束就自动释放了。

栈区大小的限制，不同的资料上有不同的说法，有个文章说申请大小的限制是1或者2M，可以自己改，但是最大不超过8M

堆区（heap）是一个程序运行动态分配的内存区域。一般由程序员分配释放，若程序员不释放，程序结束时可能由OS回收。 程序员通过new等方式创建的对象，一般会在堆区中分配内存。

我们平常说的GC，指的是对堆内存的回收。对内存的优化一般也指的是堆内存。

嗯，这段描述基本适用于多数语言了。不过c# 函数里new的struct对象（值类型）一般情况都是在栈上的，和c++不一样。然后堆的空间在64位下就没有2G或者4G的限制了。

## 内存对齐（memory alignment）

操作的内存地址（更准确的说，操作内存的起始地址）能够被操作的内存大小整除，满足这个要求的内存访问叫做访问对齐的内存（aligned memory access），否则就是访问未对齐的内存（unaligned memory access）。
https://www.cnblogs.com/zhao-zongsheng/p/9099603.html

## 内存的使用简介

在Windows平台下，高级语言写出的程序经过编译链接，最终会变成PE文件。当PE文件被装载运行后，就成了所谓的进程。
PE文件代码段中包含的二进制级别的机器代码会被装入内存的 **代码区(.text)** ，处理器将到内存的这个区域一条一条地取出指令和操作数，并送入运算逻辑单元进行运算；如果代码中请求开辟 **动态内存** ，则会在内存的 **堆区** 分配一块大小合适的区域返回给代码区的代码使用；当函数调用发生时， **函数的调用关系等信息** 会动态地保存在内存的 **栈区** ，以供处理器在执行完被调用函数的代码时，返回母函数。

## 函数调用的过程——函数栈

https://blog.csdn.net/m0_37717595/article/details/80368411

## 经典的堆栈的讲解

https://blog.csdn.net/yingms/article/details/53188974

## new和malloc的区别

  1. new 是 **运算符** malloc是 **库函数**
  2. **new和delete底层还是由malloc和free实现** 的，区别在于new和delete在生成复杂数据类型时会执行两步: **分配内存** 和 **调用构造函数** / **调用析构函数** 和 **销毁内存** 。
  3. **malloc** 申请的内存 **没有类型** ,为(void*) 需要根据使用需求进行强制转换, **new** 不需要,返回 **对应类型的指针** 。
  4. new并不是每次都会调用构造函数，对于简单类型eg.int是不需要的。

## 具体的内存分配问题

局部变量存在栈区，malloc()分配的空间在堆区。
malloc使用堆空间。
malloc 申请空间，需要标注大小，free释放的是指针指向的空间

>   * 当不需要再使用申请的内存时，记得 **释放** ，并 **且指针=NULL** ；释放后应该把指向这块内存的指针指向NULL， **防止程序后面不小心使用了它** 。
>   * free时候,指针不能发生偏移，原因是指针指向的是申请空间的首地址，而且申请的空间的大小在free源码结构体中有记录
>

例子：

```cpp
string str1 = &quot;test&quot;;
string str2 = &quot;test&quot;;

```

## Ref

https://www.cnblogs.com/houjun/p/4909413.html
https://www.cnblogs.com/qiuly/p/8576149.html
http://www.360doc.com/content/12/0102/11/1299815_176623363.shtml
https://www.zhihu.com/question/19729973
http://www.360doc.com/content/12/0102/11/1299815_176623363.shtml
https://blog.csdn.net/yingms/article/details/53188974
https://www.cnblogs.com/cchHers/p/10010275.html
https://zhuanlan.zhihu.com/p/78478567
https://www.zhihu.com/question/59238963
https://blog.csdn.net/cherrydreamsover/article/details/81627855
https://blog.csdn.net/buptzy/article/details/80149622
https://www.cnblogs.com/will-boot/p/3302936.html
