内存原理 | 内存分配 | 内存对齐

本文涉及的产品
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: 内存原理 | 内存分配 | 内存对齐

虚拟地址空间

  • 内核空间:进程管理、设备管理、虚拟文件管理
  • 用户空间
  • 栈空间 stack:存储局部变量,函数参数
  • 内存映射段 mmap:静态库与动态库,文件映射,匿名映射
  • 堆空间 heap:malloc / calloc / new
  • 读写段:全局变量、静态变量
  • 只读段:文字常量区(字符串常量)和程序代码区(二进制代码)


* 堆和栈的区别


管理方式 程序员手动控制,memory leak 编译器管理
空间大小 内存区域不连续(空闲链表),受限于虚拟地址空间,例如:32 位系统 4 G 内存区域连续,空间大小固定。操作系统预设,一般是 2 M。
内存管理 系统遍历内存空闲链表,寻找第一个大于所需空间的空闲结点,将其分配给程序。 栈的剩余空间大于申请空间,系统为程序提供内存,否则异常栈溢出。
碎片问题 频繁的内存申请释放,造成内存空间不连续,产生大量的碎片 栈后进先出,没有内存碎片
生长方向 堆向上生长,向高地址方向增长 栈向下生长,向低地址空间增长
分配方式 动态分配 系统提供静态和动态的方式
分配效率 效率低,间接寻址,第一次访问指针,第二次访问指针指向的内存 效率高,计算机底层提供支持(寄存器、指令等)


内存分配

动态内存分配系统调用

int brk(void *addr);            // 返回对的顶部地址
 void *sbrk(intptr_t increment);  // 扩展堆,increment 指定要增加的大小

先通过 sbrk 扩展堆,将这部分空闲内存空间作为缓冲池,然后通过 malloc/ free 管理缓冲池中的内存。

* malloc 原理

malloc 使用空闲链表组织堆中的空闲区块,每个内存区块由内存控制块 mem_control_block + 可用内存空间组成。malloc 返回的是内存控制块后的地址,即可用内存空间的起始地址。

struct mem_control_block {
   int is_available; // 是否可用(如果还没被分配出去,就是 1)
   int size;         // 实际空间的大小
 };

malloc 分配时遍历空闲链表,以首次适应算法为例,寻找第一个大于所需空间的空闲区块,然后将其分配给程序,返回这可用空间的指针。如果没有这样的内存块,则向操作系统申请扩展堆内存。若空闲区块空间有剩余,系统自动将多余的部分重新放入空闲链表。

常见的内存匹配算法有

  • 首次适应法
  • 最佳适应法
  • 最差适应法
  • 下一个适应法

* free 原理

free 将内存区块重新插入到空闲链表,free 只接受一个指针,却可以释放恰当大小的内存,这是因为在内存控制块保存了该区域的大小。

被 free 回收的内存首先被 ptmalloc 使用双向链表保存起来,当用户下一次申请内存时,会尝试从这些内存中寻找合适的返回,避免了频繁的系统调用,占用过多的系统资源。同时 ptmalloc 也会尝试对小块内存进行合并,避免产生过多的内存碎片。

内存对齐

内存的自然对齐:按照结构体中最大数据成员对齐。

  • 结构体内成员按照声明顺序存储,第一个成员地址和整个结构体地址相同。
  • 结构体的首地址必须是最大数据成员大小的整数倍
  • 每个成员相对于结构体首地址的偏移量必须是该成员大小的整数倍,否则填充字节。
  • 整个结构体的大小必须是最大数据成员大小的整数倍,否则填充字节。

对齐系数:#pragma pack(n)

#pragma pack(n) // 按照 n (2 的 n 次幂)个字节对齐
 #pragma pack()  // 取消自定义字节对齐方式

* 内存对齐的规则

  • 数据成员对齐规则:第一个数据成员放在偏移 0 的地方,每个数据成员的对齐按照#pragma pack(n)和该数据成员自身长度,比较小的那个进行
  • 结构(或联合)的整体对齐规则:数据成员对齐后,结构(或联合)整体也要进行对齐,对齐按照#pragma pack(n)和结构(联合)最大数据成员长度中,比较小的那个进行
  • 结构体作为成员:内部结构体成员从最大元素的整数倍和#pragma pack(n)最小的一个的整数倍地址开始存储。

例如:

struct SS {
     int a;
     char b;
     short c;
     int d;
     struct FF {
         int a1;
         char b1;
         short c1;
         char d1;
     } MyStructFF;
     int e;
     double ww; 
 } MyStructSS;

其内存对齐方式如下:

测试代码:

#include <iostream>
 /* #pragma pack(4) */
 using std::cout;
 using std::endl;
 struct x {
     char a;
     int b;
     short c;
     char d;
 } MyStructX;//12
 struct y {
     int b;
     char a;
     char d;
     short c;
 } MyStructY; // 8
 struct SS {
     int a;
     char b;
     short c;
     int d;
     struct FF {
         int a1;
         char b1;
         short c1;
         char d1;
     } MyStructFF;
 #if 1
     /* char e; // 28 */
     int e;
     double ww; // 40
 #endif
 } MyStructSS;
 struct DD {
     int a;
     char b;
     short c;
     int d;
     struct FF {
         double a1;
         char b1;
         short c1;
         char d1;
     } MyStructFF;
     char e; // 40
 } MyStructDD;
 struct GG {
     char e[2];
     short h;
     struct A {
         int a;
         double b;
         float c;
     } MyStructA;
 } MyStructGG; // 32
 int main(int argc, char **argv) {
     cout <<"sizeof(MyStructX) = " << sizeof(MyStructX) << endl;
     cout <<"sizeof(MyStructY) = " << sizeof(MyStructY) << endl;
     cout <<"sizeof(MyStructSS) = " << sizeof(MyStructSS) << endl;
     cout <<"sizeof(MyStructDD) = " << sizeof(MyStructDD) << endl;
     cout <<"sizeof(MyStructGG) = " << sizeof(MyStructGG) << endl;
     return 0;
 }


相关实践学习
基于MaxCompute的热门话题分析
Apsara Clouder大数据专项技能认证配套课程:基于MaxCompute的热门话题分析
相关文章
|
6月前
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
235 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
5月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
181 0
|
11月前
|
算法 JavaScript 前端开发
新生代和老生代内存划分的原理是什么?
【10月更文挑战第29天】新生代和老生代内存划分是JavaScript引擎为了更高效地管理内存、提高垃圾回收效率而采用的一种重要策略,它充分考虑了不同类型对象的生命周期和内存使用特点,通过不同的垃圾回收算法和晋升机制,实现了对内存的有效管理和优化。
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
191 4
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
216 3
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
247 3
|
监控 算法 Java
Java内存管理:垃圾收集器的工作原理与调优实践
在Java的世界里,内存管理是一块神秘的领域。它像是一位默默无闻的守护者,确保程序顺畅运行而不被无用对象所困扰。本文将带你一探究竟,了解垃圾收集器如何在后台无声地工作,以及如何通过调优来提升系统性能。让我们一起走进Java内存管理的迷宫,寻找提高应用性能的秘诀。
|
8月前
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
393 0
|
监控 算法 Java
深入理解Java中的垃圾回收机制在Java编程中,垃圾回收(Garbage Collection, GC)是一个核心概念,它自动管理内存,帮助开发者避免内存泄漏和溢出问题。本文将探讨Java中的垃圾回收机制,包括其基本原理、不同类型的垃圾收集器以及如何调优垃圾回收性能。通过深入浅出的方式,让读者对Java的垃圾回收有一个全面的认识。
本文详细介绍了Java中的垃圾回收机制,从基本原理到不同类型垃圾收集器的工作原理,再到实际调优策略。通过通俗易懂的语言和条理清晰的解释,帮助读者更好地理解和应用Java的垃圾回收技术,从而编写出更高效、稳定的Java应用程序。
|
缓存 Java 编译器
Go 中的内存布局和分配原理
Go 中的内存布局和分配原理

热门文章

最新文章