【QandA C++】内存泄漏、进程地址空间、堆和栈、内存对齐、大小端和判断、虚拟内存等重点知识汇总
目录
内存泄漏
内存模型 、进程地址空间
堆和栈的区别
内存对齐
大端小端及判断
虚拟内存有什么作用
内存泄漏
概念:
是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况, 内存泄漏并不是指内存在物理上的消失, 而是应用程序分配了某段内存后, 因为设计错误, 失去了对该段内存的控制, 因而造成了内存的浪费.
- new和malloc申请资源使用后, 没有用delete和free释放
- 子类继承父类时, 父类的析构函数不是虚函数
- 未关闭的文件或资源
危害:
长期运行的程序出现内存泄漏, 影响很大; 出现内存泄漏会导致响应越来越慢, 最终卡死.
避免:
- 计数法:使用new或者malloc时,让该数+1,delete或free时,该数-1,程序执行完打印这个计数,如果不为0则表示存在内存泄露
- 一定要将基类的析构函数声明为虚函数
- 对象数组的释放一定要用delete []
- 有new就有delete,有malloc就有free,保证它们一定成对出现
- 出问题了使用内存泄漏工具检测。
内存泄漏非常常见,解决方案分为两种:
- 事前预防型。如智能指针等。
- 事后查错型。如泄漏检测工具。
- Linux下可以使用Valgrind工具
- Windows下可以使用CRT库
内存模型 、进程地址空间
如上图,从低地址到高地址,用户空间内存,从低到高分别是 6 种不同的内存段:
- 代码段,包括二进制可执行代码。只读,包含一些只读的变量。
- 数据段,包括已初始化的静态常量和全局变量;
- BSS 段,包括未初始化的静态变量和全局变量;
- 堆段,包括动态分配的内存,从低地址开始向高地址增长;动态申请内存用,由new分配的内存块,其释放由程序员控制(一个new对应一个delete)
- 文件映射段,包括动态库、共享内存等,从低地址开始向上增长;最后还有一个共享区,位于堆和栈之间。
- 栈段,存储局部变量、函数参数值。栈从高地址向低地址增长。是一块连续的空间。在不需要时自动清除的存储区。
Linux下的进程地址空间具体是由mm_struct实现的
struct mm_struct{unsigned int code_start;unsigned int code_end;unsigned int init_start;unsigned int init_end;unsigned int uninit_start;unsigned int uninit_end;unsigned int heap_start;unsigned int heap_end;unsigned int stack_start;unsigned int stack_end;
};
比如堆向上增长,栈向下增长,实际上就是改变mm_struct中的堆和栈的起始指针来实现的!
为什么要有进程地址空间?
为了实现多任务操作系统中的多进程隔离和独立运行,以确保不同进程之间的互不干扰和安全性。
- 隔离和保护:每个进程都有自己独立的地址空间,使得不同进程之间的内存互不干扰。这种隔离性确保了一个进程的错误或恶意行为不会对其他进程造成影响,提高了系统的稳定性和安全性。
- 相对地址一致性:每个进程都认为自己的地址空间是从0开始的,并且认为看到的是相同的地址空间范围。这种相对地址一致性使得进程可以使用相对地址进行内存操作,而不必关心其他进程的地址空间。
- 独立内存:每个进程都认为自己独占内存,可以自由分配和管理自己的内存资源。这使得进程可以在不互相干扰的情况下运行,并且不需要担心其他进程的内存使用情况。
- 虚拟内存:进程地址空间还支持虚拟内存的概念,允许操作系统在物理内存有限的情况下为每个进程提供大于物理内存的虚拟内存空间。这通过将部分数据存储在磁盘上,根据需要进行页面调度,提高了内存利用率。
堆和栈的区别
分配方式:
栈由编译器自动分配和管理,程序员无需手动控制栈内存的分配和释放。局部变量、函数参数以及函数调用上下文等都存储在栈上。
堆由程序员手动申请和释放,通常使用new(C++)或malloc(C)等函数分配内存,并使用delete(C++)或free(C)来释放内存。堆用于存储动态分配的数据,如动态数组、对象实例等。
空间大小限制:
栈的大小通常是有限的,具体大小由编译器或操作系统设置。栈的大小在编译时或运行时可以进行配置,但总是有限的。
堆的大小受限于系统可用的虚拟内存大小,通常比栈要大得多。堆大小受限于计算机系统中有效的虚拟内存(32bit 系统理论上是4G),所以堆的空间比较灵活,比较大
栈空间和堆区的大小是由操作系统和编译器决定的,不同系统和编译器可能会有不同的默认值。一般来说,栈空间默认是1MB或2MB,而堆区一般是1GB到4GB之间。
内存管理机制:
栈的内存管理由编译器自动完成,变量的生命周期与其作用域相对应。栈内存的分配和释放是隐式的,不需要程序员干预。
堆的内存管理由程序员手动控制。程序员负责显式地分配堆内存,并在不再需要时释放它。如果不正确地管理堆内存,可能会导致内存泄漏或悬挂指针等问题。
碎片问题:
栈内存通常不会出现碎片问题,因为栈的内存分配和释放都是线性的,按照函数调用的顺序进行。
堆内存可能会出现碎片问题,特别是在频繁进行动态内存分配和释放操作时。这可能导致内存空间的不连续,影响程序的性能。
生长方向:
栈的生长方向通常是向下的,即从高地址向低地址增长。这意味着栈的顶部在分配时逐渐向较低的地址移动。
堆的生长方向通常是向上的,即从低地址向高地址增长。堆内存在动态分配时逐渐向较高的地址分配。
分配效率:
栈由编译器和操作系统提供的支持,分配和释放内存的效率较高,通常采用硬件级别的指令进行操作。
堆的内存分配和释放需要程序员显式地调用函数,效率相对较低,并可能涉及复杂的内存管理机制。
形象的比喻
栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
内存对齐
内存对齐涉及到如何存储和访问数据以提高计算机的性能和效率。确保数据按照一定的规则存储在内存中,以便于有效地访问。
结构体的对齐规则:
- 第一个成员在与结构体变量偏移量为0的地址处。(即结构体的首地址处,即对齐到0处)
- 其他成员变量要对齐到最小对齐数的整数倍的地址处。
- 结构体的总大小为最大对齐数的整数倍。
对齐数 = 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。
注:VS中的默认对齐数为8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。
要修改编译器的默认对齐数,我们需要借助于以下预处理命令:#pragma pack(...)
为什么存在内存对齐?
平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些平台只能在某些地址处取得某些特定类型的数据,否则抛出硬件异常。
- 比如,当一个平台要取一个整型数据时只能在地址为4的倍数的位置取得,那么这时就需要内存对齐,否则无法访问到该整型数据。
性能原因: 数据结构(尤其是栈)应该尽可能的在自然边界上对齐。原因在于,为了访问未对齐内存,处理器需要作两次内存访问;而对齐的内存访问仅需一次。
其实结构体的内存对齐是拿空间来换取时间的做法。
大端小端及判断
大端模式:是指数据的低位(就是权值较小的后面那几位)保存在内存的高地址中,而数据的高位,保存在内存的低地址中;地址由小向大增加,而数据从高位往低位放。
小端模式:是指数据的高位(就是权值较大的前面那几位)保存在内存的高地址中,而数据的低位,保存在内存的低地址中;地址由大向小增加,而数据从低位往高位放 。
大小端的意义在于确保数据在不同的计算机体系结构之间正确传递、解释和处理,保证系统之间的互操作性和数据的可移植性。
例如:32bit的数字0x12345678
所以在Socket编程中,往往需要将操作系统所用的小端存储的IP地址转换为大端存储,这样才能进行网络传输
小端模式中的存储方式为:
大端模式中的存储方式为:
如何在代码中进行判断呢?
方式一:使用强制类型转换-这种法子不错
#include <iostream>
using namespace std;
int main()
{int a = 0x1234;//由于int和char的长度不同,借助int型转换成char型,只会留下低地址的部分char c = (char)(a);if (c == 0x12)cout << "big endian" << endl;else if(c == 0x34)cout << "little endian" << endl;
}
方式二:巧用union联合体
#include <iostream>
using namespace std;
// union联合体的重叠式存储,endian联合体占用内存的空间为每个成员字节长度的最大值
union endian
{int a;char ch;
};
int main()
{endian value;value.a = 0x1234;//a和ch共用4字节的内存空间if (value.ch == 0x12)cout << "big endian"<<endl;else if (value.ch == 0x34)cout << "little endian"<<endl;
}
虚拟内存有什么作用
- 虚拟内存可以使得进程对运行内存超过物理内存大小
- 因为程序运行符合局部性原理,CPU 访问内存会有很明显的重复访问的倾向性,对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域。当进程需要访问被置换出去的页时,它们会被重新加载到物理内存中。这种机制允许了更大的程序运行,而不受物理内存的限制。
- 提高内存利用率:
- 虚拟内存系统可以更好地利用物理内存资源。只有进程当前需要的部分内存被加载到物理内存中,而不是将整个程序加载到内存中。这减少了内存浪费,允许多个进程在有限的物理内存中共存。
- 虚拟内存为每个进程提供了独立的地址空间
- 每个进程有自己的页表,这样,一个进程无法直接访问其他进程的内存,从而提高了安全性和隔离性。即使两个进程使用相同的虚拟地址,它们映射到不同的物理内存位置。
- 页表里的页表项中除了物理地址之外,还有一些标记属性的比特,比如控制一个页的读写权限,标记该页是否存在等。在内存访问方面,操作系统提供了更好的安全性。
相关文章:

【QandA C++】内存泄漏、进程地址空间、堆和栈、内存对齐、大小端和判断、虚拟内存等重点知识汇总
目录 内存泄漏 内存模型 、进程地址空间 堆和栈的区别 内存对齐 大端小端及判断 虚拟内存有什么作用 内存泄漏 概念: 是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况, 内存泄漏并不是指内存在物理上的消失, 而是应用程序分配了某段内存后, 因为设计错误…...

怒刷LeetCode的第12天(Java版)
目录 第一题 题目来源 题目内容 解决方法 方法一:贪心 第二题 题目来源 题目内容 解决方法 方法一:双指针 方法二:KMP算法 方法三:indexOf方法 方法四:Boyer-Moore算法 方法五:Rabin-Karp算法…...

RabbitMQ实现秒杀场景示例
本文章通过MQ队列来实现秒杀场景 整体的设计如下图,整个流程中对于发送发MQ失败和发送到死信队列的数据未做后续处理 1、首先先创建MQ的配置文件 Configuration public class RabbitConfig {public static final String DEAD_LETTER_EXCHANGE "deadLetterE…...

如何提升网站排名优化(百度SEO优化,轻松提升排名)
在当今互联网时代,拥有一个优秀的网站是很重要的。而一个网站如果能够在搜索引擎上的排名很靠前,那么将会带来更多的流量、更多的用户和更多的利润。那么如何提升网站排名优化呢?蘑菇号www.mooogu.cn 百度SEO优化的5个规则 1.关键词选取要合…...
CountDownLatch 和 CyclicBarrier 用法以及区别
在使用多线程执行任务时,通常需要在主线程进行阻塞等待,直到所有线程执行完毕,主线程才能继续向下执行,主要有以下几种可选方式 1. 调用 main 线程的 sleep 方法 一般用于预估线程的执行时间,在主线程内执行线程sleep…...

9.9喝遍“茶、奶、果、酒”,茶饮价格战是因为“无活可整”?
“家人们谁懂啊,周一瑞幸周二奈雪周三茶百道周四库迪周五古茗周六coco,9块9根本喝不完!” 紧随咖啡的9.9大战,茶饮们也在今年加速“蜜雪冰城化”,9.9变成了一种潮流。伴随着茶百道、coco、奈雪的茶等品牌把9.9玩出了更…...
echarts 学习网址
1、PPChart 网址:PPChart - 让图表更简单 2、YX-Chartlib 网址:http://chartlib.datains.cn3、isqqw 网址:echarts图表集4、makeapie 网址:makeapie echarts社区图表可视化案例5、Chart.Top 网址:chart.top - 让图…...
android源码编译
整包编译 导入环境变量 source ./build/envsetup.shlunch:选择平台编译选项make:执行编译 编译单个apk 进入到apk mk所在路径 mma...

盘点双电机驱动技术
对于电动汽车来说,双电机相对于单电机加主减速器或变速箱的方案在提高驱动效率方面的优势: 第一,单电机在低速、高速轻载等情况下,效率降低比较严重。 电动机的高效区间虽然比内燃机大得多,但是汽车的转速和转矩要求…...

ubuntu下用pycharm专业版连接AI服务器及其docker环境
一:用pycharm专业版连接AI服务器 1、首先在自己电脑上新建一个文件夹,后续用于映射服务器上自己所要用的项目文件 2、用pycharm专业版打开该文件夹,作为一个项目打开 3、然后在工具->部署->配置 4、配置中形式如下: 点击左…...
IntentFilter笔记
一、action <intent-filter>中可以有多个action,Intent只要匹配其中1个action即匹配成功<intent-filter>没有action,任何Intent无法与之匹配<intent-filter>中有action,Intent中没有action时可以与之匹配成功<intent-fi…...

【二叉树】——链式结构(快速掌握递归与刷题技巧)
📙作者简介: 清水加冰,目前大二在读,正在学习C/C、Python、操作系统、数据库等。 📘相关专栏:C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 👍…...
项目管理—项目普遍存在的问题
软件公司有开发业务,在完成一个软件产品或实施项目时,常常会出现以下的状况: 开发人员不懂客户业务,一个高大上的规划,落地后的软件,只是机械的满足了基本功能,毫无易用性和科学性可言。 项目只…...
Ubuntu Seata开机自启动服务
1、创建service文件 在/lib/systemd/system目录下创建seata.service文件 [Unit] Descriptionalibaba seata Afternetwork.target Documentationhttps://seata.io/zh-cn/[Service] Userroot Grouproot Typeforking Environment"JAVA_HOME/usr/local/programs/jdk-8u333-li…...

腾讯mini项目-【指标监控服务重构】2023-08-26
今日已办 Venus 的 Trace 无感化 定义 handler 函数 fiber.Handler 的主要处理逻辑返回处理中出现的 error返回处理中响应 json 的函数 // handler // Description: // Author xzx 2023-08-26 18:00:03 // Param c // Return error // Return func() error : function for …...
《Essential C++》之(面向过程泛型编程)
目录 🌼面向过程的编程风格 -- 第2章 🍈2.2 🍈2.4 🍈2.5 🍈2.6 🌼泛型编程风格 -- 第3章 🍍3.1 🍍3.4 前言 要求 完整代码 输入输出 🌼面向过程的编程风…...

机器学习笔记:adaBoost
1 介绍 AdaBoost(Adaptive Boosting)是一种集成学习方法,它的目标是将多个弱分类器组合成一个强分类器 通过反复修改训练数据的权重,使得之前分类错误的样本在后续的分类器中得到更多的关注每一轮中,都会增加一个新的…...

Anchor DETR
Anchor DETR(AAAI 2022) 改进: 提出了基于anchor的对象查询提出Attention变体-RCDA 在以前DETR中,目标的查询是一组可学习的embedding。然而,每个可学习的embedding都没有明确的意义 (因为是随机初始化的)ÿ…...

适合在家做的副业 整理5个,有电脑就行
今天,我们不说别的,整理5个适合个人在家单干的副业。需要电脑,如果你没电脑就不用看了,最后两个,我们也在做,你可以看到最后了解。这些副业,大家多去实践操作,前期,每月三…...
Android WebSocket
WS Android WebSocket 资源 名字资源AAR下载GitHub查看Gitee查看 Maven 1.build.grade allprojects {repositories {...maven { url https://jitpack.io }} }2./app/build.grade dependencies {implementation com.github.RelinRan:WS:2022.2023.9.23.1 }初始化 配置权…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...