C++八股——内存分配

文章目录

    • 1. 虚拟内存空间
    • 2. malloc和free
    • 3. new和delete
    • 4. 内存池

1. 虚拟内存空间

程序进程的虚拟内存空间是操作系统为每个进程提供的独立、连续的逻辑地址空间,与物理内存解耦。其核心目的是隔离进程、简化内存管理,并提供灵活的内存访问控制。

(图片取自于:【C++】内存管理分布_c++内存分布-CSDN博客)

自下而上为低地址到高地址。

以Linux x86-64架构为例:

虚拟内存空间分为用户空间内核空间

  • 用户空间(低地址 → 高地址):进程可直接访问的区域,通常占据虚拟地址空间的大部分。
  • 内核空间(高地址保留区):操作系统内核专用,进程无法直接访问,需通过系统调用交互。

用户空间分为

  • 代码段(Text Segment)

    • 地址范围0x400000 附近(起始地址)。
    • 内容:编译后的可执行机器指令(如程序的main函数)。
    • 权限:只读(Read-Only)、可执行(Execute),不可修改。
    • 特点:多个进程可共享同一代码段(如动态库)。
  • 初始化数据段(Data Segment)

    • 地址范围:紧邻代码段。
    • 内容:全局变量、静态变量(已显式初始化,如 int a = 10;)。
    • 权限:可读写(Read-Write),不可执行。
  • 未初始化数据段(BSS Segment)

    • 地址范围:紧邻初始化数据段。
    • 内容:未显式初始化的全局/静态变量(如 int b;),默认值为0。
    • 权限:可读写,不可执行。
  • 堆(Heap)

    • 地址范围:向高地址动态增长。
    • 内容:动态分配的内存(如 malloc()new)。
    • 权限:可读写,不可执行。
    • 管理:由程序员控制分配/释放,可能产生内存碎片。
  • 内存映射区域(Memory Mapping Segment)

    • 地址范围:堆与栈之间的动态区域。

    • 内容

      • 动态链接库(如libc.so)。
      • 文件映射(mmap系统调用,如加载共享内存或大文件)。
      • 匿名映射(用于大块内存分配,如某些malloc实现)。
    • 权限:可自定义(读/写/执行)。

  • 栈(Stack)

    • 地址范围:用户空间顶端附近,向低地址增长。

    • 内容

      • 函数调用栈帧(局部变量、函数参数、返回地址)。
      • 线程栈(每个线程有独立栈空间)。
    • 权限:可读写,不可执行。

    • 管理:自动分配/释放,由编译器控制,大小有限(可能栈溢出)。

  • 环境变量与命令行参数

    • 地址范围:栈的顶部区域。
    • 内容argv(命令行参数)、envp(环境变量)的字符串数组。
    • 权限:可读写。

内核空间

  • 地址范围:64位系统中通常为高地址的 0xffff800000000000 以上。
  • 内容
    • 内核代码、数据结构。
    • 进程页表、硬件驱动、中断处理程序等。
  • 权限:仅内核态可访问,用户态访问会触发段错误。

2. malloc和free

参考文章:malloc 底层实现及原理_malloc底层原理-CSDN博客

malloc

  • 当分配的大小小于128KB时,通过brk()系统调用从堆段分配内存
  • 当分配的大小大于等于128KB时,通过mmap()系统调用从文件映射段分配内存

注意:这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。


free

在实际分配内存时会多分配16字节(位于元数据之前)用来记录内存块描述信息,其中包括内存块的大小。

当调用free释放内存时,传入的指针会首先向前偏移16字节,获取内存块的信息,然后再释放。

  • 对于brk()申请的小空间,会标记为空间,并不会直接释放。当连续空闲空间大于128KB时会执行内存紧缩操作(trim)
  • 对于mmap()申请的大空间,会调用munmap()归还给操作系统

3. new和delete

参考文章:【C++】内存管理分布_c++内存分布-CSDN博客

new 的底层步骤

  • 内存分配:调用 operator new 函数(内部通常基于 malloc)申请指定大小的内存。
  • 对象构造:在分配的内存上调用对象的构造函数(初始化成员变量等)。
  • 异常处理:若内存不足,operator new 抛出 std::bad_alloc 异常(除非使用 nothrow 版本)。

底层展开:

void* ptr = operator new(sizeof(MyClass)); // 内部调用 malloc
MyClass::MyClass(ptr);                     // 构造函数

delete 的底层步骤

  • 对象析构:调用对象的析构函数(清理资源,如释放句柄)。
  • 内存释放:调用 operator delete 函数(内部通常基于 free)释放内存。

底层展开:

MyClass::~MyClass(obj);      // 析构函数
operator delete(obj);        // 内部调用 free

mallocfree的对比

特性new/deletemalloc/free
语言层面C++ 运算符,支持重载C 标准库函数,不可重载
内存初始化调用构造函数/析构函数仅分配/释放原始内存,无初始化逻辑
类型安全返回类型明确指针(如 MyClass*返回 void*,需手动类型转换
内存大小计算自动计算类型所需大小(如 new int需手动指定字节数(如 malloc(4)
错误处理内存不足时抛出异常(可捕获)返回 NULL,需检查返回值
数组支持支持 new[]delete[]需手动计算数组大小,无内置支持
底层扩展性可自定义 operator new/operator delete无法修改 malloc/free 行为
适用场景C++ 对象管理(含构造/析构)原始内存操作或与 C 代码交互

异常与错误处理

  • new:失败时抛出 std::bad_alloc,可通过 try-catch 捕获。

    try {int* arr = new int[1000000000000];
    } catch (const std::bad_alloc& e) {std::cerr << "内存不足: " << e.what() << std::endl;
    }
    
  • malloc:失败时返回 NULL,需显式检查。

    int* arr = (int*)malloc(1000000000000 * sizeof(int));
    if (arr == nullptr) {perror("malloc 失败");
    }
    

内存对齐与重载

  • new:支持自定义内存对齐(C++17 的 align_val_t)和重载。

    // 自定义 operator new
    void* operator new(size_t size, const char* tag) {std::cout << "通过标签分配: " << tag << std::endl;return malloc(size);
    }
    MyClass* obj = new ("DEBUG") MyClass();
    
  • malloc:对齐由实现决定,无法直接定制。

数组处理

  • new[]:自动计算数组元素总大小,并为每个元素调用构造函数。

    MyClass* arr = new MyClass[5]; // 调用 5 次构造函数
    delete[] arr;                   // 调用 5 次析构函数
    
  • malloc:需手动计算总字节数,且不构造对象。

    MyClass* arr = (MyClass*)malloc(5 * sizeof(MyClass));
    free(arr); // 不会调用析构函数,可能导致资源泄漏
    

4. 内存池

内存池(Memory Pool)是一种预先分配并统一管理内存资源的技术,旨在优化动态内存分配的效率和性能。

4.1 基本概念

  • 预先分配:在程序初始化阶段,一次性向操作系统申请一大块连续内存(称为“池”)。
  • 自主管理:程序自行管理池内的内存分配与回收,避免频繁调用系统级函数(如malloc/free)。
  • 按需分配:从池中划分小块内存供程序使用,释放时标记为可复用而非立即归还操作系统。

4.2 核心优势

特性传统malloc/free内存池
分配速度需遍历复杂数据结构(如Bins)直接定位空闲块,速度极快
内存碎片易产生外部/内部碎片碎片可控,甚至完全消除(固定块)
系统调用开销频繁调用brk/mmap仅初始化和销毁时调用
线程安全全局锁可能引发竞争可设计为线程私有池或无锁结构
适用场景通用、动态需求高频次、固定/可预测内存需求

4.3 实现方式

固定大小内存池

  • 机制:池中所有内存块大小相同(如4KB)。

  • 管理:使用空闲链表(Free List)跟踪可用块。

    struct MemoryBlock {MemoryBlock* next; // 指向下一个空闲块
    };
    MemoryBlock* free_list; // 空闲链表头
    
  • 操作

    • 分配:从链表头部取一个块,时间复杂度O(1)。
    • 释放:将块插回链表头部,时间复杂度O(1)。
  • 优点:零碎片、极快分配。

  • 缺点:无法处理变长需求,可能浪费内存。

可变大小内存池

  • 机制:支持不同大小的内存请求。
  • 管理
    • 分离空闲链表:为不同大小范围维护多个链表(如8B、16B、32B…)。
    • 伙伴系统(Buddy System):按2的幂次分割内存块,合并相邻空闲块。
      • 分配时向上取整到最近的2^n大小。
      • 释放时检查“伙伴块”是否空闲,若空闲则合并。
  • 优点:灵活支持变长请求,减少碎片。
  • 缺点:管理复杂度高,存在内部碎片。

4.4 典型应用场景

  • 高频次小对象分配

    • 如网络服务器为每个请求分配临时缓冲区。
    • 示例:Nginx使用内存池管理HTTP请求资源。
  • 实时系统

    确保内存分配时间确定性,避免传统malloc的不可预测延迟。

  • 游戏开发

    快速创建/销毁大量游戏实体(如子弹、粒子效果),通过对象池(Object Pool)实现。

  • 嵌入式系统

    资源受限环境,需严格控制内存使用和碎片。

4.5 示例

#define BLOCK_SIZE 64     // 固定块大小
#define POOL_SIZE 100     // 池中块数量typedef struct MemoryBlock {struct MemoryBlock* next;
} MemoryBlock;MemoryBlock* free_list = NULL;// 初始化内存池
void init_pool() {static char pool[BLOCK_SIZE * POOL_SIZE];for (int i = 0; i < POOL_SIZE; i++) {MemoryBlock* block = (MemoryBlock*)(pool + i * BLOCK_SIZE);block->next = free_list;free_list = block;}
}// 分配内存
void* pool_alloc() {if (!free_list) return NULL; // 池耗尽MemoryBlock* block = free_list;free_list = free_list->next;return (void*)block;
}// 释放内存
void pool_free(void* ptr) {MemoryBlock* block = (MemoryBlock*)ptr;block->next = free_list;free_list = block;
}

(注:以上内容参考自DeepSeek)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80543.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux基础】网络相关命令

目录 netstat命令 1.1 命令介绍 1.2 命令格式 1.3 常用选项 1.4 常用命令实例 1.4.1 显示所有TCP连接 1.4.2 查看路由表 1.4.3 实时监控网络接口流量 1.4.4 查看监听中的端口以及关联进程 ping命令 2.1 命令介绍 2.2 命令格式 2.3 常用选项 2.4 常用示例 ifconfi…

adb 实用命令汇总

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 基础adb命令 # 重启adb adb kill-server# 查看已连接的设备 adb devices# 进入命令行 adb shell# 使用 -s 参数来指定设备 adb -s <设备序列号> shell…

C#管道通讯及传输信息丢失的原因

以下是C#管道通讯客户端/服务端共用类 namespace PipeCommunication { /// <summary> /// 管道信息回调通知 /// </summary> /// <param name"msg"></param> public delegate void PipeMessageEventHandler(string msg…

MixTeX - 支持CPU推理的多模态LaTeX OCR

文章目录 一、项目概览相关资源核心特性技术特点 二、安装三、使用说明环境要求 四、版本更新五、当前限制 一、项目概览 MixTeX是一款创新的多模态LaTeX识别小程序&#xff0c;支持本地离线环境下的高效CPU推理。 无论是LaTeX公式、表格还是混合文本&#xff0c;MixTeX都能轻…

简单 Linux 字符设备驱动程序

注&#xff1a;本文为 “Linux 字符设备驱动” 相关文章合辑。 英文引文&#xff0c;机翻未校。 中文引文&#xff0c;略作重排。 未整理去重&#xff0c;如有内容异常&#xff0c;请看原文。 Simple Linux character device driver 简单 Linux 字符设备驱动程序 Oleg Kutko…

NX949NX952美光科技闪存NX961NX964

NX949NX952美光科技闪存NX961NX964 在半导体存储领域&#xff0c;美光科技始终扮演着技术引领者的角色。其NX系列闪存产品线凭借卓越的性能与创新设计&#xff0c;成为数据中心、人工智能、高端消费电子等场景的核心组件。本文将围绕NX949、NX952、NX961及NX964四款代表性产品…

协议路由与路由协议

协议路由”和“路由协议”听起来相似&#xff0c;但其实是两个完全不同的网络概念。下面我来分别解释&#xff1a; 一、协议路由&#xff08;Policy-Based Routing&#xff0c;PBR&#xff09; ✅ 定义&#xff1a; 协议路由是指 根据预设策略&#xff08;策略路由&#xff0…

Linux510 ssh服务 ssh连接

arning: Permanently added ‘11.1.1.100’ (ECDSA) to the list of known hosts. rooot11.1.1.100’s password: Permission denied, please try again. rooot11.1.1.100’s password: Permission denied, please try again 还没生效 登不上了 失效了 sshcaozx26成功登录 …

金融学知识笔记

金融学知识笔记 一、引言 金融学它结合了数学、概率论、统计学、经济学和计算机科学等多学科的知识&#xff0c;用于解决金融领域中的各种问题&#xff0c;如金融衍生品定价、投资组合优化、风险管理和固定收益证券分析等。通过对金融学的学习&#xff0c;我们可以更好地理解…

AB测试面试题

AB测试面试题 常考AB测试问答题(1)AB测试的优缺点是什么?(2)AB测试的一般流程/介绍一下日常工作中你是如何做A/B实验的?(3)第一类错误 vs 第二类错误 vs 你怎么理解AB测试中的第一、二类错误?(4)统计显著=实际显著?(5)AB测试效果统计上不显著?(6)实验组优于对…

USR-M100采集数据并提交MQTT服务器

本文为记录备忘&#xff0c;不做过多解释。 模块自身带有2路数字量输入&#xff0c;2路模拟量输入&#xff0c;2路485接口 数字量接报警输入&#xff0c;模拟量接压力传感器&#xff0c;液位传感器&#xff0c;485接口分别接流量计&#xff0c;温湿度传感器。 正确接线&…

Octave 绘图快速入门指南

目录 1. 基本的 2D 绘图 2. 自定义图形样式 3. 绘制散点图 4. 绘制柱状图 5. 绘制直方图 6. 3D 绘图 6.6.1 3D 曲面图 6.6.2 3D 散点图 7. 绘制极坐标 8. 多子图绘制 总结 Octave 是一个类似于 MATLAB 的开源数学软件&#xff0c;广泛用于数值计算和数据分析。它提供…

RabbitMQ--基础篇

RabbitMQ 简介&#xff1a;RabbitMQ 是一种开源的消息队列中间件&#xff0c;你可以把它想象成一个高效的“邮局”。它专门负责在不同应用程序之间传递消息&#xff0c;让系统各部分能松耦合地协作 优势&#xff1a; 异步处理&#xff1a;比如用户注册后&#xff0c;主程序将发…

【MySQL】事务(重点)

目录 一、什么是事务&#xff1a; 二、事务的前置知识了解 引擎是否支持事务 事务的提交方式 事务操作的前置准备&#xff1a; 三、事务回滚&#xff1a; 四、事务崩溃&#xff1a; 原子性&#xff1a; 持久性&#xff1a; 五、自动提交和手动提交&#xff1a; 六、…

C++STL——stack,queue

stack与queue 前言容器适配器deque 前言 本篇主要讲解stack与queue的底层&#xff0c;但并不会进行实现&#xff0c;stack的接口 queue的接口 &#xff0c;关于stack与queue的接口在这里不做讲解&#xff0c;因为通过前面的对STL的学习&#xff0c;这些接口都是大同小异的。 …

STM32智能手表:基于FreeRTOS

引言 随着物联网和可穿戴设备的快速发展&#xff0c;智能手表作为典型代表&#xff0c;集成了传感器数据采集、实时显示、无线通信等多项功能。本文将深入剖析一个基于STM32和FreeRTOS的智能手表项目&#xff0c;从硬件架构到软件设计&#xff0c;逐步讲解如何构建一个完整的嵌…

leetcode504.七进制数

标签&#xff1a;进制转换 机试真题 给定一个整数 num&#xff0c;将其转化为 7 进制&#xff0c;并以字符串形式输出。 示例 1: 输入: num 100 输出: "202" 示例 2: 输入: num -7 输出: "-10" 思路&#xff1a;求n进制就是循环取余数&#xff0c;…

中国古代史2

夏朝&#xff08;公元前2070-公元前1600年&#xff09; 1.禹建立了我国历史上第一个奴隶制国家–夏朝&#xff0c;定都阳城。禹传启&#xff0c;世袭制代替禅让制。 2.夏代都城&#xff1a;二里头遗址位于今河南洛阳偃师二里头村。发现了大型绿松石龙形器&#xff0c;被命名为…

死锁的形成

死锁的形成 背景学习资源死锁的本质 背景 面试可能会被问到. 学习资源 一个案例: https://www.bilibili.com/video/BV1pz421Y7kM 死锁的本质 互相持有对方的资源. 存在资源竞争都没有释放. 可能出现死锁. insert into demo_user (no, name) values (6, ‘test1’) on dupl…

MapReduce架构-打包运行

&#xff08;一&#xff09;maven打包 MapReduce是一个分布式运算程序的编程框架&#xff0c;是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff08;例如&#xff1a;jar…