进程的哪些内存类型容易引起内存泄漏

相信你在平时的工作中,应该遇到过下面这些场景: 伴随着服务器中的后台任务持续地运行,系统中可用内存越来越少; 应用程序正在运行时忽然被 OOM kill 掉了; 进程看起来没有消耗多少内存,但是系统内存就是不够用了; ……

类似问题,很可能就是内存泄漏导致的。我们都知道,内存泄漏指的是内存被分配出去后 一直没有被释放,导致这部分内存无法被再次使用,甚至更加严重的是,指向这块内存空间的指针都不存在了,进而再也无法访问这块内存空间。

我们平时遇到的内存泄漏可能是应用程序的内存泄漏,也可能是内核(操作系统)的内存 泄漏;而应用程序的内存泄漏可能是堆内存(heap)的泄漏,也可能是内存映射区 (Memory Mapping Region)的泄漏。这些不同类型的内存泄漏,它们的表现形式也是 不一样的,解决方案也不一样,所以为了更好地处理内存泄漏问题,我们首先就需要去了 解这些不同的内存类型。 这些不同的内存类型都可以理解为是进程地址空间 (Address Space) 的一部分,那地址空 间是怎么工作的呢?

1. 进程的地址空间

我们用一张图,来表示进程的地址空间。图的左侧是说进程可以通过什么方式来更改进程 虚拟地址空间,而中间就是进程虚拟地址空间是如何划分的,右侧则是进程的虚拟地址空 间所对应的物理内存或者说物理地址空间。

image-20250122215916197

首先,应用程序会调用内存申请释放相关的函数,比如 glibc 提供的 malloc(3)、 free(3)、 calloc(3) 等;或者是直接使用系统调用 mmap(2)、munmap(2)、 brk(2)、sbrk(2) 等。如果使用的是库函数,这些库函数其实最终也是对系统调用的封装,所以可以理解为是应 用程序动态申请释放内存,最终是要经过 mmap(2)、munmap(2)、brk(2)、sbrk(2) 等这 些系统调用。当然从库函数到系统调用,这其中还涉及到这些库本身进行的一些内存层面 的优化,比如说,malloc(3) 既可能调用 mmap(2),又可能会调用 brk(2)。

然后,这些内存申请和释放相关的系统调用会修改进程的地址空间 (address space),其中 brk(2) 和 sbrk(2) 修改的是 heap(堆),而 mmap(2) 和 munmap(2) 修改的是 Memory Mapping Region(内存映射区)。请注意这些针对的都是虚拟地址,应用程序都是跟虚拟地址打交道,不会直接跟物理地址 打交道。而虚拟地址最终都要转换为物理地址,由于 Linux 都是使用 Page(页)来进行管 理的,所以这个过程叫 Paging(分页)。

我们用一张表格来简单汇总下这些不同的申请方式所对应的不同内存类型,这张表格也包含了我在前文讲的 Page Cache,所以你可以把它理解为是进程申请内存的类型大汇总:

image-20250122220109419

进程运行所需要的内存类型有很多种,总的来说,这些内存类型可以从是不是文件映射, 以及是不是私有内存这两个不同的维度来做区分,也就是可以划分为上面所列的四类内存:

  1. 私有匿名内存。进程的堆、栈,以及 mmap(MAP_ANON | MAP_PRIVATE) 这种方式 申请的内存都属于这种类型的内存。其中栈是由操作系统来进行管理的,应用程序无需 关注它的申请和释放;堆和私有匿名映射则是由应用程序(程序员)来进行管理的,它 们的申请和释放都是由应用程序来负责的,所以它们是容易产生内存泄漏的地方。
  2. 共享匿名内存。进程通过 mmap(MAP_ANON | MAP_SHARED) 这种方式来申请的内 存,比如说 tmpfs 和 shm。这个类型的内存也是由应用程序来进行管理的,所以也可能会发生内存泄漏。
  3. 私有文件映射。进程通过 mmap(MAP_FILE | MAP_PRIVATE) 这种方式来申请的内存, 比如进程将共享库(Shared libraries)和可执行文件的代码段(Text Segment)映射到自己的地址空间就是通过这种方式。对于共享库和可执行文件的代码段的映射,这是 通过操作系统来进行管理的,应用程序无需关注它们的申请和释放。而应用程序直接通 过 mmap(MAP_FILE | MAP_PRIVATE) 来申请的内存则是需要应用程序自己来进行管 理,这也是可能会发生内存泄漏的地方。
  4. 共享文件映射。进程通过 mmap(MAP_FILE | MAP_SHARED) 这种方式来申请的内存, 我们在上一个模块课程中讲到的 File Page Cache 就属于这类内存。这部分内存也需要 应用程序来申请和释放,所以也存在内存泄漏的可能性。

了解了进程虚拟地址空间这些不同的内存类型后,我们来继续看下它们对应的物理内存。进程虚拟地址空间是通过 Paging(分页)这种方式来映射为物理内存的,进程调用 malloc() 或者 mmap() 来申请的内存都是虚拟内存,只有往这些内存中写入数据后(比如通过 memset),才会真正地分配物理内存 。

你可能会有疑问,如果进程只是调用 malloc() 或者 mmap() 而不去写这些地址,即不去给 它分配物理内存,是不是就不用担心内存泄漏了? 答案是这依然需要关注内存泄露,因为这可能导致进程虚拟地址空间耗尽,即虚拟地址空间同样存在内存泄露的问题。

接下来,我们继续用一张图片来细化一下分页的过程:

image-20250122220409263

如上图所示,Paging 的大致过程是,CPU 将要请求的虚拟地址传给 MMU(Memory Management Unit,内存管理单元),然后 MMU 先在高速缓存 TLB(Translation Lookaside Buffer,页表缓存)中查找转换关系,如果找到了相应的物理地址则直接访 问;如果找不到则在地址转换表(Page Table)里查找计算。最终进程访问的虚拟地址就 对应到了实际的物理地址。

了解了地址空间的相关知识之后,你就能够对进程的地址空间做一个合理的规划,或者说合理的控制了。这样出现问题时,不至于产生太严重的影响,你可以把规划好进程的地址 空间理解为是进程内存问题的兜底方案。Linux 上最典型的规划进程地址空间的方式就是通过 ulimit,你可以通过调配它,来规划进程最大的虚拟地址空间、物理地址空间、栈空间 是多少,等等。 对于进程地址空间相关的知识我们先聊到这里,接下来我们看下如何使用工具来观察进程的地址空间。

2. 用数据观察进程的内存

学会观察进程地址空间是分析内存泄漏问题的前提,当你怀疑内存有泄漏时,首先需要去 观察哪些内存在持续增长,哪些内存特别大,这样才能够判断出内存泄漏大致是出在哪 里,然后针对性地去做分析;相反,如果你在没有仔细观察进程地址空间之前,就盲目猜 测问题出在哪,处理问题很可能会浪费大量时间,甚至会南辕北辙。 那么都有哪些观察进程的工具呢?

我们常用来观察进程内存的工具,比如说 pmap、ps、 top 等,都可以很好地来观察进程的内存。

首先,我们可以使用 top 来观察系统所有进程的内存使用概况,打开 top 后,然后按 g 再输 入 3,从而进入内存模式就可以了。在内存模式中,我们可以看到各个进程内存的 %MEM、VIRT、RES、CODE、DATA、SHR、nMaj、nDRT,这些信息通过 strace 来跟 踪 top 进程,你会发现这些信息都是从 /proc/[pid]/statm 和 /proc/[pid]/stat 这个文件 里面读取的:

$ strace -p `pidof top`
  open("/proc/16348/statm", O_RDONLY) = 9
  read(9, "40509 1143 956 24 0 324 0\n", 1024) = 26
  close(9)  = 0
  ...
  open("/proc/16366/stat", O_RDONLY) = 9
  read(9, "16366 (kworker/u16:1-events_unbo"..., 1024) = 182
  close(9)

除了 nMaj(Major Page Fault, 主缺页中断,指内容不在内存中然后从磁盘中来读取的 页数)外,%MEM 则是从 RES 计算而来的,其余的内存信息都是从 statm 文件里面读取 的,如下是 top 命令中的字段和 statm 中字段的对应关系:

image-20250122220737641

另外,有些时候所有进程的 RES 相加起来要比系统总的物理内存大,这是因为 RES 中有一些内存是被一些进程给共享的。 在明白了系统中各个进程的内存使用概况后,如果想要继续看某个进程的内存使用细节, 你可以使用 pmap。如下是 pmap 来展示 sshd 进程地址空间里的部分内容:

$  pmap -x `pidof sshd`
Address           Kbytes     RSS     Dirty   Mode      Mapping 
000055e798e1d000   768       652       0     r-x--      sshd
000055e7990dc000   16		16		  16    r----      sshd   
000055e7990e0000    4         4         4    rw---      sshd
000055e7990e1000   40        40        40    rw---      [ anon ]
...
00007f189613a000   1800      1624       0    r-x--       libc-2.17.so
00007f18962fc000   2048         0       0    -----       libc-2.17.so
00007f18964fc000     16        16      16    r----       libc-2.17.so  
00007f1896500000      8         8       8    rw---       libc-2.17.so    
...
00007ffd9d30f000    132        40      40    rw---       [ stack ]  
...

每一行表示一种类型的内存(Virtual Memory Area),每一列的含义如下:

  1. Mapping,用来表示文件映射中占用内存的文件,比如 sshd 这个可执行文件,或者堆 [heap],或者栈[stack],或者其他,等等。
  2. Mode,它是该内存的权限,比如,“r-x”是可读可执行,它往往是代码段 (Text Segment);“rw-”是可读可写,这部分往往是数据段 (Data Segment);“r–”是只 读,这往往是数据段中的只读部分。
  3. Address、Kbytes、RSS、Dirty,Address 和 Kbytes 分别表示起始地址和虚拟内存 的大小,RSS(Resident Set Size)则表示虚拟内存中已经分配的物理内存的大小, Dirty 则表示内存中数据未同步到磁盘的字节数。

可以看到,通过 pmap 我们能够清楚地观察一个进程的整个的地址空间,包括它们分配的 物理内存大小,这非常有助于我们对进程的内存使用概况做一个大致的判断。比如说,如 果地址空间中[heap]太大,那有可能是堆内存产生了泄漏;再比如说,如果进程地址空间 包含太多的 vma(可以把 maps 中的每一行理解为一个 vma),那很可能是应用程序调用 了很多 mmap 而没有 munmap;再比如持续观察地址空间的变化,如果发现某些项在持 续增长,那很可能是那里存在问题。

pmap 同样也是解析的 /proc 里的文件,具体文件是 /proc/[pid]/maps 和 /proc/[pid]/smaps,其中 smaps 文件相比 maps 的内容更详细,可以理解为是对 maps 的一个扩展。你可以对比 /proc/[pid]/maps 和 pmaps 的输出,你会发现二者的内容是一 致的。

除了观察进程自身的内存外,我们还可以观察进程分配的内存和系统指标的关联,我们就 以常用的 /proc/meminfo 为例,来说明我们上面提到的四种内存类型(私有匿名,私有 文件,共享匿名,共享文件)是如何体现在系统指标中的:

image-20250122221707682

如上图所示,凡是私有的内存都会体现在 /proc/meminfo 中的 AnonPages 这一项,凡 是共享的内存都会体现在 Cached 这一项,匿名共享的则还会体现在 Shmem 这一项。

$ cat /proc/meminfo
  ...
  Cached:          3799380 kB
  ...
  AnonPages:       1060684 kB
  ...
  Shmem:           8724 KB   
  ...

结束语

本文讲述了进程内存管理相关的一些知识,包括进程的虚拟内存与物理内存,要点如下:

  1. 进程直接读写的都是虚拟地址,虚拟地址最终会通过 Paging(分页)来转换为物理内存 的地址,Paging 这个过程是由内核来完成的。
  2. 进程的内存类型可以从 anon(匿名)与 file(文件)、private(私有)与 shared(共 享)这四项来区分为 4 种不同的类型,进程相关的所有内存都是这几种方式的不同组合。
  3. 查看进程内存时,可以先使用 top 来看系统中各个进程的内存使用概况,再使用 pmap 去观察某个进程的内存细节。

ared(共 享)这四项来区分为 4 种不同的类型,进程相关的所有内存都是这几种方式的不同组合。
3. 查看进程内存时,可以先使用 top 来看系统中各个进程的内存使用概况,再使用 pmap 去观察某个进程的内存细节。

进程的内存管理涉及到非常多的术语,对于常用的一些术语,比如 VIRT、RES、SHR 等, 你还是需要牢记它们的含义的,只有熟练掌握了它们的含义,你在分析内存问题时才会更 加地得心应手。比如说,如果 RES 太高而 SHR 不高,那可能是堆内存泄漏;如果 SHR 很高,那可能是 tmpfs/shm 之类的数据在持续增长,如果 VIRT 很高而 RES 很小,那可能 是进程不停地在申请内存,但是却没有对这些内存进行任何的读写操作,即虚拟地址空间 存在内存泄漏。 同样地,我希望你自己可以写一些测试用例来观察这些指标的变化。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何给自己的域名配置免费的HTTPS How to configure free HTTPS for your domain name

今天有小伙伴给我发私信,你的 https 到期啦 并且随手丢给我一个截图。 还真到期了。 javapub.net.cn 这个网站作为一个用爱发电的编程学习网站,用来存编程知识和面试题等,平时我都用业余时间来维护,并且还自费买了服务器和阿里云…

Glarysoft Malware Hunter 多语检测和删除各种恶意软件和间谍软件 v1.195.0.824

Glarysoft Malware Hunter 是一款专业的安全工具,旨在帮助用户检测和删除各种恶意软件和间谍软件。它可以扫描和删除计算机上的病毒、木马、广告软件和其他安全威胁。 软件功能 病毒扫描:Malware Hunter可以快速而全面地扫描计算机,以查找潜…

通过Ukey或者OTP动态口令实现windows安全登录

通过 安当SLA(System Login Agent)实现Windows安全登录认证,是一种基于双因素认证(2FA)的解决方案,旨在提升 Windows 系统的登录安全性。以下是详细的实现方法和步骤: 1. 安当SLA的核心功能 安…

Windows远程连接Docker服务

问题背景 本地开发了一个SpringBoot项目,想通过Docker部署起来,我本地是Window11系统,由于某些原因不能虚拟化并且未安装Docker-Desktop,所以我在想有没有办法本地不需要虚拟化也不需要安装Docker-Desktop来实现支持Docker命令远…

Ubuntu20.04 运行 Cartographer demo bag

官方文档: Running Cartographer ROS on a demo bag — Cartographer ROS documentation Running Cartographer ROS on a demo bag Now that Cartographer and Cartographer’s ROS integration are installed, you can download example bags (e.g. 2D and 3D b…

【R语言】流程控制

一、流程控制 R语言中&#xff0c;常用的流程控制函数有&#xff1a;repeat、while、for、if…else、switch。 1、repeat循环 repeat函数经常与 break 语句或 next 语句一起使用。 repeat ({x <- sample(c(1:7),1)message("x ", x, ",你好吗&#xff1f…

2025年最新深度学习环境搭建:Win11+ cuDNN + CUDA + Pytorch +深度学习环境配置保姆级教程

本文目录 一、查看驱动版本1.1 查看显卡驱动1.2 显卡驱动和CUDA对应版本1.3 Pytorch和Python对应的版本1.4 Pytorch和CUDA对应的版本 二、安装CUDA三、安装cuDANN四、安装pytorch五、验证是否安装成功 一、查看驱动版本 1.1 查看显卡驱动 输入命令nvidia-smi可以查看对应的驱…

Go学习:常量

变量&#xff1a;程序运行期间&#xff0c;可以改变的量&#xff0c;变量声明需要使用 var 常量&#xff1a;程序运行期间&#xff0c;不可以改变的量&#xff0c;常量声明需要使用 const 目录 1. 常量不允许修改 2. 常量赋值不使用 : 3. 常量能够自动推导类型 1. 常量不允许…

字符串和正则表达式(System.String类)

在C#string关键字实际上指向.NET基类System.String。System.String是一个功能非常强大且用途非常广泛的基类&#xff0c;但它不是.NET库中唯一与字符串相关的类。 主要内容&#xff1a; 创建字符串——如果多次修改一个字符串&#xff0c;例如&#xff0c;在显示字符串或将其传…

WPF实战案例 | C# WPF实现大学选课系统

WPF实战案例 | C# WPF实现大学选课系统 一、设计来源1.1 主界面1.2 登录界面1.3 新增课程界面1.4 修改密码界面 二、效果和源码2.1 界面设计&#xff08;XAML&#xff09;2.2 代码逻辑&#xff08;C#&#xff09; 源码下载更多优质源码分享 作者&#xff1a;xcLeigh 文章地址&a…

对数的换底公式及其证明

一、换底公式 二、证明 设 &#xff0c;由于对数和指数之间可以相互转换&#xff0c;不难得到&#xff1a;。 将 等式两边分别取以c为底的对数&#xff0c;得到&#xff1a; 联立&#xff08;1&#xff09;&#xff08;2&#xff09;式&#xff0c;得到&#xff1a; &#x…

STM32补充——IAP

0 前置知识&#xff1a; FLASH相关内容&#xff1a;前往STM32补充——FLASH STM32三种烧录方式&#xff08;看看就行&#xff09;&#xff1a; 1.ISP&#xff1a;In System Programming&#xff08;在系统编程&#xff09; 执行芯片厂商的 Bootloader 程序进入 ISP 模式&…

【2024年华为OD机试】(C/D卷,200分)- 5G网络建设 (JavaScriptJava PythonC/C++)

一、问题描述 题目描述 现需要在某城市进行5G网络建设&#xff0c;已经选取N个地点设置5G基站&#xff0c;编号固定为1到N。接下来需要各个基站之间使用光纤进行连接以确保基站能互联互通。不同基站之间假设光纤的成本各不相同&#xff0c;且有些节点之间已经存在光纤相连。 …

计算机毕业设计hadoop+spark股票基金推荐系统 股票基金预测系统 股票基金可视化系统 股票基金数据分析 股票基金大数据 股票基金爬虫

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

【Linux】深刻理解动静态库

1.什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复⽤的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个⼈的代码都从零开始&#xff0c;因此库的存在意义⾮同寻常。本质上来说库是⼀种可执⾏代码的⼆进制形式&#xff0c;可以被操作系统载…

CrypTen——基于pytorch的隐私保护机器学习框架

目录 一、CrypTen概述 二、应用场景 三、CrypTen优势 四、CrypTen技术解析 1.基于pytorch的构建基础 2.核心密码学原语 3.加密模型训练流程 五、传统隐私保护技术与CrypTen的对比 1.传统隐私保护技术介绍 2.CrypTen与传统隐私保护技术的区别 六、CrypTen的环境配置…

他把智能科技引入现代农业领域

江苏田倍丰农业科技有限公司&#xff08;以下简称“田倍丰”&#xff09;是一家专注于粮油种植的农业科技公司&#xff0c;为拥有300亩以上田地的大户提供全面的解决方案。田倍丰通过与当地政府合作&#xff0c;将土地承包给大户&#xff0c;并提供农资和技术&#xff0c;实现利…

电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测

电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测 目录 电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测预测效果基本描述程序设计参考资料 预测效果 基本描述 电池预测 | 第22讲 基于GRU-Attention的锂电池剩余寿命预测 锂电池作为现代电子设备的重要动力…

Spring Boot AOP实现动态数据脱敏

依赖&配置 <!-- Spring Boot AOP起步依赖 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>/*** Author: 说淑人* Date: 2025/1/18 23:03* Desc…

UE虚幻引擎No Google Play Store Key:No OBB found报错如何处理?

问题描述&#xff1a; UE成功打包APK并安装过后&#xff0c;启动应用时提示&#xff1a; No Google Play Store KeyNo OBB found and no store key to try to download. Please setone up in Android Project SettingsUE配置默认在打包APK时会附加生成一个OBB文件&#xff0c;…