进程内存分布--之理论知识

一个由C/C++编译的程序占用的内存分为以下几个部分 :

1、栈区(stack):由编译器自动分配释放 ,存放函数调用函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

2、堆区(heap):一般由程序员分配释放,如malloc 来分配的全局指针。若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后有系统释放。 

4、文字常量区 :常量字符串就是放在这里的。 程序结束后由系统释放。 

5、程序代码区:存放函数体的二进制代码。

  • 进程内存分布总结如下:
  • - 程序段 (Text Segment):可执行文件代码的内存映射
    • 程序代码在内存中的映射,存放函数体的二进制代码。
    • 可执行代码、字符串字面值、只读变量
  • - 数据段 (Data Segment):可执行文件的已初始化全局变量的内存映射
    • 在程序运行初已经对变量进行初始化的数据。
    • 已初始化且初值非0的全局变量和局部静态变量,全局静态变量,常量
  • - BSS段 (BSS Segment):未初始化的全局变量或者静态变量(用零页初始化)
    • 在程序运行初未对变量进行初始化的数据。
    • 未初始化或初值为0的全局变量和静态局部变量
  • - 堆区 (Heap) : 存储动态内存分配,匿名的内存映射
    • 存储动态内存分配,需要程序员手工分配,手工释放.
    • 注意它与数据结构中的堆是两回事,分配方式类似于链表
  • - 栈区 (Stack) : 进程用户空间栈,由编译器自动分配释放,存放函数的参数值、局部变量的值等
    • 存储局部、临时变量,函数参数
    • 函数调用时,存储函数的返回指针,用于控制函数的调用和返回。
    • 在程序块开始时自动分配内存,结束时自动释放内存,其操作方式类似于数据结构中的栈。
    • 但不包括static声明的变量, static 意味着 在“数据段”中 存放变量
  • - 映射段(Memory Mapping Segment):任何内存映射文件
    • 内核将文件的内容直接映射到内存
    • 内存映射是一种方便高效的文件I/O方式,所以它被用来加载动态库。创建一个不对应于任何文件的匿名内存映射也是可能的,此方法用于存放程序的数据。
    • 该区域用于映射可执行文件用到的动态链接库。

         Linux 对进程地址空间有个标准布局,地址空间中由各个不同的内存段组成 (Memory Segment),主要的内存段如下:图示如下:

        Linux 内核将这 4G 字节的空间分为两部分,将最高的 1G 字节(0xC0000000-0xFFFFFFFF)供内核使用,称为 内核空间。而将较低的3G字节(0x00000000-0xBFFFFFFF)供各个进程使用,称为 用户空间。每个进程可以通过系统调用陷入内核态,因此内核空间是由所有进程共享的。虽然说内核和用户态进程占用了这么大地址空间,但是并不意味它们使用了这么多物理内存,仅表示它可以支配这么大的地址空间。它们是根据需要,将物理内存映射到虚拟地址空间中使用。

        在Linux中,内核空间是持续存在的,并且在所有进程中都映射到同样的物理内存。(Linux内核由系统内的所有进程共享)。

 而上面进程虚拟地址空间中的栈区,正指的是我们所说的进程栈。进程栈的初始化大小是由编译器和链接器计算出来的,但是栈的实时大小并不是固定的,Linux 内核会根据入栈情况对栈区进行动态增长(其实也就是添加新的页表)。但是并不是说栈区可以无限增长,它也有最大限制 RLIMIT_STACK (一般为 8M),我们可以通过 ulimit 来查看或更改 RLIMIT_STACK 的值。

  • 线程的内存分布

现代 linux 有多线程(linux 的线程其实是个轻量级的进程),一个进程的多个线程之间共享全局变量、堆、打开的文件…,但栈是不能共享的:栈中各层函数帧代表着一条执行线索,一个线程是一条执行线索,所以每个线程独占一个栈,而这些栈又都必须在所属进程的内存空间中。进程的内存分布就变成了下面这个样子:

2: 内存分区结构

  1. 代码区
  2. 数据区
    1. 静态存储区
      1. 全局变量区
      2. 静态变量区
      3. 常量区(静态常量区)
        1. 字符串常量
        2. 常变量区

  • 可执行二进制程序 = 代码段(text)+数据段(data)+BSS段
    • 而当程序被加载到内存单元时,则需要另外两个域:堆域和栈域。
    • 一个正在运行的C程序占用的内存区域分为代码段、初始化数据段、未初始化数据段(BSS)、堆、栈5个部分。

  • 正在运行的C程序 = 代码段(text)+初始化数据段(data)+未初始化数据段(BSS)+堆(heap)+栈(stack)
    • 在将应用程序加载到内存空间执行时,操作系统负责代码段、数据段和BSS段的加载,并将在内存中为这些段分配空间。
    • 栈亦由操作系统分配和管理,而不需要程序员显示地管理;
    • 堆段由程序员自己管理,即显示地申请和释放空间。

  • 动态分配与静态分配,二者最大的区别在于:
    • 直到Run-Time(运行)的时候,执行动态分配
    • 而在compile-time(编译)的时候,就已经决定好了分配多少Text+Data+BSS+Stack。
    • 通过malloc()动态分配的内存,需要程序员手工调用free()释放内存,否则容易导致内存泄露,
    • 而静态分配的内存则在进程执行结束后系统释放(Text, Data), 但Stack段中的数据很短暂,函数退出立即被销毁。

进程和线程内存

我们都知道进程运行时,会有一个栈空间(stack)和一个堆空间(heap), 栈空间用于函数调用和局部变量,堆空间是C语言的 malloc 来分配的全局指针。

这些都是进程的私有数据,除了这些,还有映射进来的动态库,进程间的共享内存等共享空间。另外,操作系统还支持预留虚拟地址空间的功能(延迟分配),也就是在读写该内存的时候,操作系统才进行物理内存的分配,因此进程占用的空间情况还是比较复杂的。下面简单地说明一下。

  1. VSZ:Virtual Memory Size(虚拟内存大小)。进程占用的全部地址空间,共享库,预分配内存,交换分区等都包含在里面。因此,它远远大于实际的占用的内存空间。
  2. RSS:Resident Set Size(驻留集大小), 实际占用的物理内存,它包含共享库,但不包含在交换分区的空间。随着程序的运行,RSS也会跟着增长,VSZ将是它的上限。
  3. PSS:Proportional Set Size, 也是实际分配的物理内存,与RSS的区别是,它以平分的方式来计算共享库的大小(共享库 / 进程个数), RSS会把共享库的大小全部计算进来。
  4. USS:Unique Set Size, 进程的私有内存(独自使用的库,堆等空间),不包含共享的内存空间。
  5. ANON: Anonymous memory,匿名内存 —— 没有文件关联的内存页面。Linux会自动映射文件到内存,读取的文件后,会自动缓存到内存。如果,应用程序只是使用mmap(MAP_ANONYMOUS) 分配一些内存页面没有文件关联,就称为“匿名内存”。
  6. Dirty: dirty pages , 脏页大小 —— 还没有写回到硬盘的缓存页面。
  7. VIRT: 同VSZ。
  8. RES: 同RSS。

内存指标

Item

全称

含义

等价

USS

Unique Set Size

物理内存

进程独占的内存

PSS

Proportional Set Size

物理内存

Pss =Uss+按比例包含共享库

RSS

Resient Set Size

物理内存

RSS=USS+包含共享库

VSS

Virtual Set Size

虚拟内存

VSS = RSS+未分配实际物理内存

内存的大小关系:VSS>RSS>=PSS>=USS

在实际分析中,一般是以PSS的内存为准,且也是最符合实际情况的统计值

假如进程A(2k),只依赖动态库B(1000k) ;A 分配 128k的匿名空间,200k的堆栈和堆的空间——实际使用100k。其中动态库B被 2个进程共享,实际加载200k,那么 ——

VSZ = 2k + 1000k + 128k + 200k = 1230k

RSS = 2k + 200k + 128k + 100k = 430k

PSS = 2k + 200k / 2 + 128k + 100k = 330k

USS = 2k + 128k + 100k = 230k

ANON = 128k

区别

VSS : Virtual Set Size 虚拟耗用内存(包含共享库占用的内存),即单个进程全部可访问的地址空间,其大小可能包括还尚未在内存中驻留的部分。对于确定单个进程实际内存使用大小,VSS用处不大。

RSS : Resident Set Size 实际使用物理内存(包含共享库占用的内存),即单个进程实际占用的内存大小,RSS不太准确的地方在于它包括该进程所使用共享库全部内存大小。对于一个共享库,可能被多个进程使用,实际该共享库只会被装入内存一次。

PSS : Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)PSS相对于RSS计算共享库内存大小是按比例的。N个进程共享,该库对PSS大小的贡献只有1/N。

USS : Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)即单个进程私有的内存大小,即该进程独占的内存部分。USS揭示了运行一个特定进程在的真实内存增量大小。如果进程终止,USS就是实际被返还给系统的内存大小。

    PS:下一篇介绍:进程内存分布--之单线程和多线程编写代码来内存分布呈现memory-layout.cpp 

    关注我,后续还有更多专题博文分享,谢谢!!!  

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

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

    相关文章

    WEB安全--内网渗透--LMNTLM基础

    一、前言 LM Hash和NTLM Hash是Windows系统中的两种加密算法,不过LM Hash加密算法存在缺陷,在Windows Vista 和 Windows Server 2008开始,默认情况下只存储NTLM Hash,LM Hash将不再存在。所以我们会着重分析NTLM Hash。 在我们内…

    时尚优雅奢华品牌包装徽标设计衬线英文字体安装包 Kagea – Luxury Women Ligature Font

    Kagea 是一种复杂的衬线字体,有常规和压缩两种样式,具有 50 连字和替代字体,并支持多种语言。其精致的比例和别致的字体使其成为高端品牌、编辑布局、高档包装、时尚品牌和奢侈品设计的理想选择。 包含内容: TTF、OTF 和 WOFF 格…

    cut命令用法

    cut 是 Linux/Unix 系统中一个用于按列提取文本内容的命令,常用于处理结构化文本(如 CSV、日志、配置文件等)。它通过分隔符、字符位置或字节位置来切割文本,提取指定部分。 核心功能 按字段(列)提取&#…

    美国mlb与韩国mlb的关系·棒球9号位

    MLB(Major League Baseball,美国职业棒球大联盟)作为全球最高水平的职业棒球联赛,与韩国市场流行的“MLB”时尚品牌之间存在着授权合作关系,但两者在业务范畴和品牌定位上存在显著差异。 一、品牌授权背景:…

    从吉卜力漫画到艺术创造:GPT-4o多种风格绘图Prompt大全

    在3月底,GPT-4o掀起了一阵吉卜力绘图浪潮,大家纷纷输入一张图片,让4o模型进行风格化迁移,其中吉卜力风格的漫画在社交媒体上最为火热。在大家争议4o的训练数据是否侵权和4o背后的技术原理的时候,我们先来玩一玩&#x…

    Leetcode 3510. Minimum Pair Removal to Sort Array II

    Leetcode 3510. Minimum Pair Removal to Sort Array II 1. 解题思路2. 代码实现 题目链接:3510. Minimum Pair Removal to Sort Array II 1. 解题思路 这一题和题目3507. Minimum Pair Removal to Sort Array I本质上是同一道题目,唯一的区别在于时间…

    【数学建模】(时间序列模型)ARIMA时间序列模型

    ARIMA时间序列模型详解及常见时间序列模型概览 文章目录 ARIMA时间序列模型详解及常见时间序列模型概览1 引言2 ARIMA模型的基本概念3 ARIMA模型的组成部分详解3.1 AR模型 (自回归模型)3.2 MA模型 (移动平均模型)3 I (差分) 4 ARIMA模型的建模步骤5 Python实现ARIMA模型6 常见时…

    嵌入式AI开发者职业成长路线图

    嵌入式AI开发者职业成长路线图 一、核心技术能力构建 1. 深度学习框架 TensorFlow/TensorFlow Lite:适合部署到嵌入式设备PyTorch:研究和原型开发ONNX:模型转换与部署 2. 模型理解与应用 卷积神经网络(CNN):图像识别、目标检…

    单元测试之mockito

    简介 mockito是一款模拟测试框架,用于Java开发中的单元测试。通过mockito,可以创建和配置一个对象,通过它来替换对象的外部依赖。 作用:模拟一个类的外部依赖,保证单元测试的独立性。例如,在类A中会调用类…

    Oracle数据库数据编程SQL<5 正则表达式函数*****>

    Oracle 提供了一组强大的正则表达式函数,用于在 SQL 和 PL/SQL 中进行复杂的模式匹配和文本处理。这些函数基于 POSIX 标准正则表达式,功能强大且灵活。 目录 一、Oracle 正则表达式函数概览 二、函数详解及示例 1. REGEXP_LIKE 2. REGEXP_INSTR 3. REGEXP_SUBSTR 4. …

    el-tabs添加按钮增加点击禁止样式

    前置文章 一、vue使用element-ui自定义样式思路分享【实操】 二、vue3&ts&el-tabs多个tab表单校验 现状确认 点击添加按钮,没有点击样式,用户感知不明显没有限制最大的tab添加数量,可以无限添加 调整目标&代码编写 调整目标…

    DB-Mysql中TIMESTAMP与DATETIME的区别

    文章目录 ‌存储范围‌‌时区处理‌存储空间‌默认值和自动更新‌‌零值处理‌适用场景‌总结 在MySQL中,TIMESTAMP和DATETIME是两种常用的日期时间数据类型,它们虽然都用于存储日期和时间,但在多个方面存在显著差异。以下是它们的主要区别&a…

    Spring 中有哪些设计模式?

    🧠 一、Spring 中常见的设计模式 设计模式类型Spring 中的应用场景单例模式创建型默认 Bean 是单例的工厂模式创建型BeanFactory、FactoryBean抽象工厂模式创建型ApplicationContext 提供多个工厂接口代理模式结构型AOP 动态代理(JDK/CGLIB)…

    C# Winform 入门(3)之尺寸同比例缩放

    放大前 放大后 1.定义当前窗体的宽度和高度 private float x;//定义当前窗体的宽度private float y;//定义当前窗台的高度 2.接收当前窗体的尺寸大小 x this.Width;//存储原始宽度ythis.Height;//存储原始高度setTag(this);//为控件设置 Tag 属性 3.声明方法,获…

    从零开始的编程-java篇1.6.3

    前言: 通过实践而发现真理,又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识,又从理性认识而能动地指导革命实践,改造主观世界和客观世界。实践、认识、再实践、再认识,这种形式,循环往…

    【Redis】数据的淘汰策略

    目录 淘汰策略方案(8种) LRU和LFU策略的区别 使用建议 手搓LRU算法 方式一 方式二 大家好,我是jstart千语。今天和大家回来聊一下redis,这次要讲的是它的淘汰策略。为什么需要淘汰策略呢,就是当redis里面的内存占…

    【前端】Node.js一本通

    近两天更新完毕,建议关注收藏点赞。 目录 复习Node.js概述使用fs文件系统模块path路径模块 http模块 复习 为什么JS可以在浏览器中执行 原理:待执行的JS代码->JS解析引擎 不同的浏览器使用不同的 JavaScript 解析引擎:其中,C…

    【AI论文】JavisDiT: 具备层次化时空先验同步机制的联合音视频扩散Transformer

    摘要:本文介绍了一种新型的联合音频-视频扩散变换器JavisDiT,该变换器专为同步音频-视频生成(JAVG)而设计。 基于强大的扩散变换器(DiT)架构,JavisDiT能够根据开放式用户提示同时生成高质量的音…

    Java-实现公有字段自动注入(创建人、创建时间、修改人、修改时间)

    文章目录 Mybatis-plus实现自动注入定义 MetaObjectHandler配置 MyBatis-Plus 使用 MetaObjectHandler实体类字段注解使用服务类进行操作测试 Jpa启用审计功能实现自动注入添加依赖启动类启用审计功能实现AuditorAware接口实体类中使用审计注解 总结 自动注入创建人、创建时间、…

    金融机构开源软件风险管理体系建设

    开源软件为金融行业带来了创新活力的同时,也引入了一系列独特的风险。金融机构需要构建系统化的风险管理体系,以识别和应对开源软件在全生命周期中的各种风险点。下面我们将解析开源软件在金融场景下的主要风险类别,并探讨如何建立健全的风险…