【QandA C++】内存泄漏、进程地址空间、堆和栈、内存对齐、大小端和判断、虚拟内存等重点知识汇总

目录

内存泄漏

内存模型 、进程地址空间

堆和栈的区别

内存对齐

大端小端及判断

虚拟内存有什么作用


内存泄漏

概念:

是指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况, 内存泄漏并不是指内存在物理上的消失, 而是应用程序分配了某段内存后, 因为设计错误, 失去了对该段内存的控制, 因而造成了内存的浪费.

  1. new和malloc申请资源使用后, 没有用delete和free释放
  2. 子类继承父类时, 父类的析构函数不是虚函数
  3. 未关闭的文件或资源

危害:

长期运行的程序出现内存泄漏, 影响很大; 出现内存泄漏会导致响应越来越慢, 最终卡死.

避免:

  • 计数法:使用new或者malloc时,让该数+1,delete或free时,该数-1,程序执行完打印这个计数,如果不为0则表示存在内存泄露
  • 一定要将基类的析构函数声明为虚函数
  • 对象数组的释放一定要用delete []
  • 有new就有delete,有malloc就有free,保证它们一定成对出现
  • 出问题了使用内存泄漏工具检测。

内存泄漏非常常见,解决方案分为两种:

  1. 事前预防型。如智能指针等。
  2. 事后查错型。如泄漏检测工具。
  • 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中的堆和栈的起始指针来实现的!

为什么要有进程地址空间?

为了实现多任务操作系统中的多进程隔离和独立运行,以确保不同进程之间的互不干扰和安全性。

  1. 隔离和保护:每个进程都有自己独立的地址空间,使得不同进程之间的内存互不干扰。这种隔离性确保了一个进程的错误或恶意行为不会对其他进程造成影响,提高了系统的稳定性和安全性。
  2. 相对地址一致性:每个进程都认为自己的地址空间是从0开始的,并且认为看到的是相同的地址空间范围。这种相对地址一致性使得进程可以使用相对地址进行内存操作,而不必关心其他进程的地址空间。
  3. 独立内存:每个进程都认为自己独占内存,可以自由分配和管理自己的内存资源。这使得进程可以在不互相干扰的情况下运行,并且不需要担心其他进程的内存使用情况。
  4. 虚拟内存:进程地址空间还支持虚拟内存的概念,允许操作系统在物理内存有限的情况下为每个进程提供大于物理内存的虚拟内存空间。这通过将部分数据存储在磁盘上,根据需要进行页面调度,提高了内存利用率。

堆和栈的区别

分配方式:

栈由编译器自动分配和管理,程序员无需手动控制栈内存的分配和释放。局部变量、函数参数以及函数调用上下文等都存储在栈上。

堆由程序员手动申请和释放,通常使用new(C++)或malloc(C)等函数分配内存,并使用delete(C++)或free(C)来释放内存。堆用于存储动态分配的数据,如动态数组、对象实例等。

空间大小限制:

栈的大小通常是有限的,具体大小由编译器或操作系统设置。栈的大小在编译时或运行时可以进行配置,但总是有限的。

堆的大小受限于系统可用的虚拟内存大小,通常比栈要大得多。堆大小受限于计算机系统中有效的虚拟内存(32bit 系统理论上是4G),所以堆的空间比较灵活,比较大

栈空间和堆区的大小是由操作系统和编译器决定的,不同系统和编译器可能会有不同的默认值。一般来说,栈空间默认是1MB或2MB,而堆区一般是1GB到4GB之间。

内存管理机制:

栈的内存管理由编译器自动完成,变量的生命周期与其作用域相对应。栈内存的分配和释放是隐式的,不需要程序员干预。

堆的内存管理由程序员手动控制。程序员负责显式地分配堆内存,并在不再需要时释放它。如果不正确地管理堆内存,可能会导致内存泄漏或悬挂指针等问题。

碎片问题:

栈内存通常不会出现碎片问题,因为栈的内存分配和释放都是线性的,按照函数调用的顺序进行。

堆内存可能会出现碎片问题,特别是在频繁进行动态内存分配和释放操作时。这可能导致内存空间的不连续,影响程序的性能。

生长方向:

栈的生长方向通常是向下的,即从高地址向低地址增长。这意味着栈的顶部在分配时逐渐向较低的地址移动。

堆的生长方向通常是向上的,即从低地址向高地址增长。堆内存在动态分配时逐渐向较高的地址分配。

分配效率:

栈由编译器和操作系统提供的支持,分配和释放内存的效率较高,通常采用硬件级别的指令进行操作。

堆的内存分配和释放需要程序员显式地调用函数,效率相对较低,并可能涉及复杂的内存管理机制。

形象的比喻

栈就像我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。

堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。

内存对齐

内存对齐涉及到如何存储和访问数据以提高计算机的性能和效率。确保数据按照一定的规则存储在内存中,以便于有效地访问

结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。(即结构体的首地址处,即对齐到0处)
  2. 其他成员变量要对齐到最小对齐数的整数倍的地址处。
  3. 结构体的总大小为最大对齐数的整数倍

对齐数 = 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。

注: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 区域。当进程需要访问被置换出去的页时,它们会被重新加载到物理内存中。这种机制允许了更大的程序运行,而不受物理内存的限制。
  • 提高内存利用率:
  • 虚拟内存系统可以更好地利用物理内存资源。只有进程当前需要的部分内存被加载到物理内存中,而不是将整个程序加载到内存中。这减少了内存浪费,允许多个进程在有限的物理内存中共存。
  • 虚拟内存为每个进程提供了独立的地址空间
  • 每个进程有自己的页表,这样,一个进程无法直接访问其他进程的内存,从而提高了安全性和隔离性。即使两个进程使用相同的虚拟地址,它们映射到不同的物理内存位置。
  • 页表里的页表项中除了物理地址之外,还有一些标记属性的比特,比如控制一个页的读写权限,标记该页是否存在等。在内存访问方面,操作系统提供了更好的安全性。

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

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

相关文章

Leetcode---363周赛

题目列表 2859. 计算 K 置位下标对应元素的和 2860. 让所有学生保持开心的分组方法数 2861. 最大合金数 2862. 完全子集的最大元素和 一、计算k置为下标对应元素的和 简单题&#xff0c;直接暴力模拟&#xff0c;代码如下 class Solution { public:int sumIndicesWithKS…

什么是关系模型? 关系模型的基本概念

关系模型由IBM公司研究员Edgar Frank Codd于1970年发表的论文中提出&#xff0c;经过多年的发展&#xff0c;已经成为目前最常用、最重要的模型之一。 在关系模型中有一些基本的概念&#xff0c;具体如下。 (1)关系(Relation)。关系一词与数学领域有关&#xff0c;它是集合基…

第二届全国高校计算机技能竞赛——Java赛道

第二届全国高校计算机技能竞赛——Java赛道 小赛跳高 签到题 import java.util.*; public class Main{public static void main(String []args) {Scanner sc new Scanner(System.in);double n sc.nextDouble();for(int i 0; i < 4; i) {n n * 0.9;}System.out.printf(&…

Jenkins+Allure+Pytest的持续集成

一、配置 allure 环境变量 1、下载 allure是一个命令行工具&#xff0c;可以去 github 下载最新版&#xff1a;https://github.com/allure-framework/allure2/releases 2、解压到本地 3、配置环境变量 复制路径如&#xff1a;F:\allure-2.13.7\bin 环境变量、Path、添加 F:\a…

Linux系统离线安装Python

目录 一、简介 二、前提准备 三、下载Python源码 四、将离线python包传输到Linux主机 五、编译以及创建软链接 一、简介 由于工作原因&#xff0c;我们经常会在内网环境下使用Linux&#xff0c;不过这样会让我们安装一些软件变得困难&#xff0c;例如需要安装Python。虽然…

解决Pycharm使用Conda激活环境失败的问题

Q:公司电脑终端使用powershell来激活conda环境时报错? 同时手动打开powershell报"profile.ps1” 无法被加载的错误 A: 1,手动打开powershell&#xff0c;设置管理员打开 2,打开powershell 打开 PowerShell 终端&#xff0c;并输入以下命令&#xff1a;Get-ExecutionPo…

气导耳机是什么样的?盘点五款好用的气传导耳机分享

​气传导耳机在运动、户外、办公等场景中具有独特的优势。然而&#xff0c;面对市场上琳琅满目的气传导耳机产品&#xff0c;很多用户不知如何下手。接下来&#xff0c;我将推荐市面上热销火爆&#xff0c;并性能出色、性价比高的气传导耳机给大家&#xff0c;希望大家都能选到…

项目开发过程中,成员提离职,怎么办?

之前写过一篇《如何应对核心员工提离职》反响特别好&#xff0c;今天做个延展篇&#xff0c;在项目过程中&#xff0c;员工突然提离职&#xff0c;我们有什么办法让项目按时按质的上线。 项目做多了&#xff0c;总会碰到这种情况。这里给大家介绍一个解决项目问题的分析方法&a…

Hbuilder本地调试微信H5项目(二)--添加UView框架插件

摘要 在一个已创建的Hbuilder项目中&#xff0c;添加uView框架插件 前置准备 已安装Hbuilder 已创建uni-app的H5默认模板项目 实现逻辑 在Hbuilder官网找到组件说明页面 下载插件并导入HbuilderX 具体实现 访问网站 访问网址Hbuilder的uView1.8.6版本说明页 或者访问…

@Cacheable 注解(指定缓存位置)

一、Cacheable的作用 1、缓存使用步骤&#xff1a;Cacheable这个注解&#xff0c;用它就是为了使用缓存的。所以我们可以先说一下缓存的使用步骤&#xff1a; 1、开启基于注解的缓存&#xff0c;使用 EnableCaching 标识在 SpringBoot 的主启动类上。 2、标注缓存注解即可 使用…

JVM

JVM JVM设计的初心&#xff0c;是为了让java程序员感知不到系统层面的一些内容&#xff0c;让程序员只关注业务逻辑&#xff0c;不关注底层的实现细节。后来有一本书叫《深入了解java虚拟机》&#xff0c;这本书里面讨论了一些jvm话题&#xff0c;于是就掀起了jvm潮流。 1.JV…

Python语法之条件语句(很详细)

目录 Python条件语句的介绍 定义 if的语法和实例(最基本的) 语法 gif动态图展示 具体实例 实现思路&#xff1a; if-elif-else的语法和实例&#xff08;最基本的&#xff09; 语法 具体实例 实现思路&#xff1a; 判断需要多个条件需同时判断语法和实例&#xff08;最基…

华为云云耀云服务器L实例评测 | 云服务器搭建自己的gitlab代码仓库手把手教学

&#x1f4cb; 前言 &#x1f5b1; 博客主页&#xff1a;在下马农的碎碎念&#x1f917; 欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd;✍ 本文由在下马农原创&#xff0c;首发于CSDN&#x1f4c6; 首发时间&#xff1a;2023/09/26&#x1f4c5; 最近更新时…

【STM32单片机】u8g2智能风扇设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用STM32F103C8T6单片机控制器&#xff0c;使用按键、IIC OLED模块、DS18B20温度传感器、直流电机、红外遥控等。 主要功能&#xff1a; 初始化后进入温度显示界面&#xff0c;系统初始状态为手动…

医院陪诊小程序源码 陪诊陪护小程序源码

医院陪诊小程序源码 陪诊陪护小程序源码 近年来&#xff0c;随着互联网技术的不断发展&#xff0c;我们的生活中出现了越来越多的智能设备和智能应用&#xff0c;这些智能应用不仅极大方便了我们的生活&#xff0c;还对现代医疗服务体验产生了深远的影响。本文将为大家介绍一种…

JVM G1垃圾回收器学习笔记

前言 最近在工作中遇到频繁FullGC且YoungGC时间有时特别长的情况&#xff0c;而自己对JVM的垃圾回收也是一知半解&#xff0c;因此需要对JVM做系统的了解&#xff0c;为快速解决工作中的问题&#xff0c;能有效分析GC日志和业务代码&#xff0c;先从G1垃圾回收器开始学习&…

贪心算法-

代码随想录 什么是贪心 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。 这么说有点抽象&#xff0c;来举一个例子&#xff1a; 例如&#xff0c;有一堆钞票&#xff0c;你可以拿走十张&#xff0c;如果想达到最大的金额&#xff0c;你要怎么拿&#xff…

linux驱动之input子系统简述

文章目录 一、什么是input子系统二、内核代码三、代码分析 一、什么是input子系统 Input驱动程序是linux输入设备的驱动程序&#xff0c;我们最常见的就按键&#xff0c;触摸&#xff0c;插拔耳机这些。其中事件设备驱动程序是目前通用的驱动程序&#xff0c;可支持键盘、鼠标…

邮件营销方案

互联网的快速发展&#xff0c;使得新媒体营销、短视频营销、微信营销等新型营销方式成为主流。但是邮件营销仍然是性价比很高的营销方式之一&#xff0c;它不仅可以帮助你与潜在客户建立联系、传达信息并促进销售&#xff0c;同时也是维系老客户的重要手段之一。特别是对于外贸…

解答嵌入式和单片机的关系

嵌入式系统是一种特殊的计算机系统&#xff0c;用于特定任务或功能。而单片机则是嵌入式系统的核心部件之一&#xff0c;是一种在单个芯片上集成了处理器、内存、输入输出接口等功能的微控制器。刚刚好我这里有一套单片机保姆式教学&#xff0c;里面有编程教学、问题讲解、语言…