当前位置: 首页 > news >正文

内存泄漏与SWAP

内存泄漏如何发生

没正确回收分配后的内存,导致了泄漏。

访问的是已分配内存边界外的地址,导致程序异常退出

用户空间内存包括多个不同的内存段,比如只读 段、数据段、堆、栈以及文件映射段等。这些内存段正是应用程序使用内存的基本方式。

只读段、数据段、栈不会泄漏

在程序中定义了一个局部变量,比如一个整数数组 int data[64] ,就定义了 一个可以存储 64 个整数的内存段。由于这是一个局部变量,它会从内存空间的栈中分配内存。栈内存由系统自动分配和管理。一旦程序运行超出了这个局部变量的作用域,栈内存就会被 系统自动回收,所以不会产生内存泄漏的问题。

只读段,包括程序的代码和常量,由于是只读的,不会再去分配新的内存,所以也不会产 生内存泄漏。

数据段,包括全局变量和静态变量,这些变量在定义时就已经确定了大小,所以也不会产 生内存泄漏。

堆和文件映射可能会泄漏

事先并不知道数据大小,所以你就要用到标准库函数 malloc() ,在程序中动态分配内存。这时候,系统就会从内存空间的堆中分配内存。堆内存由应用程序自己来分配和管理。除非程序退出,这些堆内存并不会被系统自动释放, 而是需要应用程序明确调用库函数 free() 来释放它们。如果应用程序没有正确释放堆内 存,就会造成内存泄漏。

最后一个内存映射段,包括动态链接库和共享内存,其中共享内存由程序动态分配和管 理。所以,如果程序在分配后忘了回收,就会导致跟堆内存类似的泄漏问题。

内存泄漏导致的问题--使用SWAP、IO性能、OOM

不仅应用程序自己不能访问,系统也不能把它们再次分配给其他应用。内存泄漏不断累积,甚至会耗尽系统内存。虽然,系统最终可以通过 OOM (Out of Memory)机制杀死进程,但进程在 OOM 前, 可能已经引发了一连串的反应,导致严重的性能问题。

比如,其他需要内存的进程,可能无法分配新的内存;内存不足,又会触发系统的缓存回收 以及 SWAP 机制,从而进一步导致 I/O 的性能问题等等。

内存泄漏定位和解决

1625620565342-5a79a985-46b1-4740-aba0-5990a5b28fda.png

看到内存分配的调用栈,查看源码,想办法修复它。

常见泄漏问题:

  • malloc() 和 free() 通常并不是成对出现,而是需要你,在每个异常处理路径和成功路径 上都释放内存 。
  • 在多线程程序中,一个线程中分配的内存,可能会在另一个线程中访问和释放。 调用库函数 _free() 来 _ 释放它们。
  • 更复杂的是,在第三方的库函数中,隐式分配的内存可能需要应用程序显式释放。

SWAP 原理

当发生了内存泄漏时,或者运行了大内存的应用程序,导致系统的内存资 源紧张时,其实会导致两种可能结果,内存回收和 OOM 杀死进程。

文件页

系统释放掉可以回收的内存,比如我前面 讲过的缓存和缓冲区,就属于可回收内存。它们在内存管理中,通常被叫做文件页(Filebacked Page)。

大部分文件页,都可以直接回收,以后有需要时,再从磁盘重新读取就可以了。而那些被应 用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能 进行内存释放。

这些脏页,一般可以通过两种方式写入磁盘。

  • 可以在应用程序中,通过系统调用 fsync ,把脏页同步到磁盘中;
  • 也可以交给系统,由内核线程 pdflush 负责这些脏页的刷新。

除了缓存和缓冲区,通过内存映射获取的文件映射页,也是一种常见的文件页。它也可以被 释放掉,下次再访问的时候,从文件重新读取。

匿名页

应用程序动态分配的堆内存,在内存管理中说到的匿名页(Anonymous Page),这些 内存自然不能直接释放。

Linux 的 Swap 机制。Swap 把这些不常访问的内存先写到磁盘中,然后释 放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以 了。

Swap 说白了就是把一块磁盘空间或者一个本地文件(以下讲解以磁盘为例), 当成内存来使用。它包括换出和换入两个过程。

  • 换出,就是把进程暂时不用的内存数据存储到磁盘中,并释放这些数据占用的内存。
  • 换入,则是在进程再次访问这些内存的时候,把它们从磁盘读到内存中来。

即使服务器的内存不足,也可以使用SWAP运行大内存的应用程序。

内存再大,对应用程序来说,也有不够用的时候。

一个很典型的场景就是,即使内存不足时,有些应用程序也并不想被 OOM 杀死,而是希 望能缓一段时间,等待人工介入,或者等系统自动释放其他进程的内存,再分配给它。

常见的笔记本电脑的休眠和快速开机的功能,也基于 Swap 。休眠时,把 系统的内存存入磁盘,这样等到再次开机时,只要从磁盘中加载内存就可以。这样就省去了 很多应用程序的初始化过程,加快了开机速度。

直接内存回收、定期内存回收

一个最容易想到的场景就是,有新的大块内存分配请求,但是剩余内存不足。这个时候系统 就需要回收一部分内存(比如前面提到的缓存),进而尽可能地满足新内存请求。这个过程 通常被称为直接内存回收。

除了直接内存回收,还有一个专门的内核线程用来定期回收内存,也就是kswapd0。为了 衡量内存的使用情况,kswapd0 定义了三个内存阈值(watermark,也称为水位),分别是页最小阈值(pages_min)、页低阈值(pages_low)和页高阈值(pages_high)。剩余 内存,则使用 pages_free 表示。kswapd0 定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内 存的回收操作。

1625621369508-cadca51f-36fb-4cc0-af70-6998b9ec9342.png

  • 剩余内存小于页最小阈值,说明进程可用内存都耗尽了,只有内核才可以分配内存。
  • 剩余内存落在页最小阈值和页低阈值中间,说明内存压力比较大,剩余内存不多了。这时 kswapd0 会执行内存回收,直到剩余内存大于高阈值为止。
  • 剩余内存落在页低阈值和页高阈值中间,说明内存有一定压力,但还可以满足新内存请 求。
  • 剩余内存大于页高阈值,说明剩余内存比较多,没有内存压力。

一旦剩余内存小于页低阈值,就会触发内存的回收。这个页低阈值,其实可 以通过内核选项 **/proc/sys/vm/min_free_kbytes** 来间接设置。**min_free_kbytes** 设置了 页最小阈值,而其他两个阈值,都是根据页最小阈值计算生成的,计算方法如下 :

**pages_low = pages_min*5/4**

**pages_high = pages_min*3/2**

NUMA

系统剩余内存还多着呢。也会发生 Swap,正是处理器的 NUMA (Non-Uniform Memory Access)架构导致的。

在 NUMA 架构下,多个处理器被划分到不 同 Node 上,且每个 Node 都拥有自己的本地内存空间。

而同一个 Node 内部的内存空间,实际上又可以进一步分为不同的内存域(Zone),比如 直接内存访问区(DMA)、普通内存区(NORMAL)、伪内存区(MOVABLE)等,如下 图所示:

1625621710936-ecaf4085-1cc9-4fc2-8351-c78156171794.png

既然 NUMA 架构下的每个 Node 都有自己的本地内存空间,那么,在分析内存的使用时,也应该针对每个 Node 单独分析。 可以通过 numactl 命令,来查看处理器在 Node 的分布情况,以及每个 Node 的内存使 用情况。

1625621920795-4b4a34ce-3a46-458f-bb6a-480ac29f42ed.png

系统中只有一个 Node,也就是 Node 0 ,而且编号为 0 和 1 的两个 CPU, 都位于 Node 0 上。另外,Node 0 的内存大小为 7977 MB,剩余内存为 4416 MB。

/proc/zoneinfo

实际上,前面提到的三个内存阈值(页最小阈值、页低阈值和页高阈值),都可以通过内存 域在 proc 文件系统中的接口 /proc/zoneinfo 来查看。

pages 处的 min、low、high,就是上面提到的三个内存阈值,而 free 是剩余内存页 数,它跟后面的 nr_free_pages 相同。

1625622030825-a400bf9d-8040-4345-b8e0-3ab10ed55047.png

nr_zone_active_anon 和 nr_zone_inactive_anon,分别是活跃和非活跃的匿名页数。

nr_zone_active_file 和 nr_zone_inactive_file,分别是活跃和非活跃的文件页数。

从这个输出结果可以发现,剩余内存远大于页高阈值,所以此时的 kswapd0 不会回收内存。

当然,某个 Node 内存不足时,系统可以从其他 Node 寻找空闲内存,也可以从本地内存 中回收内存。具体选哪种模式,你可以通过 /proc/sys/vm/zone_reclaim_mode 来调整。 它支持以下几个选项:

  • 默认的 0 ,也就是刚刚提到的模式,表示既可以从其他 Node 寻找空闲内存,也可以从 本地回收内存。
  • 1、2、4 都表示只回收本地内存,2 表示可以回写脏数据回收内存,4 表示可以用 Swap 方式回收内存。

/proc/sys/vm/swappiness 调整使用 Swap 的积极程度

回收的内存既包括了文件页,又包括了匿 名页。

对文件页的回收,当然就是直接回收缓存,或者把脏页写回磁盘后再回收。

而对匿名页的回收,其实就是通过 Swap 机制,把它们写入磁盘后再释放内存。

Linux 提供了一个 /proc/sys/vm/swappiness 选项,用来调整使用 Swap 的积极程度。 swappiness 的范围是 0-100,数值越大,越积极使用 Swap,也就是更倾向于回收匿名页;数值越小,越消极使用 Swap,也就是更倾向于回收文件页。这并不是内存的百分比,而是调整 Swap 积极程度的权重,即使你把它设置成 0,当剩余内存 + 文件页小于页高阈值时,还是会发 生 Swap。

案例(20里面可以再复习)

开启swap

Linux 本身支持两种类型的 Swap,即 Swap 分区和 Swap 文件。以 Swap 文件为例,在第一个终端中运行下面的命令开启 Swap,我这里配置 Swap 文件的大小为 8GB:

1625622599788-266a65ed-a741-41a5-b148-aa356154bc37.png

再执行 free 命令,确认 Swap 配置成功:free 输出中,Swap 空间以及剩余空间都从 0 变成了 8GB,说明 Swap 已经正常开 启。

模拟大文件读请求

在第一个终端中,运行下面的 dd 命令,模拟大文件的读取:

1625622661883-1071671c-e982-4b2a-9f42-6bdacfdc49de.png

sar查看内存指标变化

在第二个终端中运行 sar 命令,查看内存各个指标的变化情况。你可以多观察一会 儿,查看这些指标的变化情况。

1625622721166-2255181d-7be3-4456-87d1-950cf4b5818e.png

sar 的输出结果是两个表格,第一个表格表示内存的使用情况,第二个表格 表示 Swap 的使用情况。其中,各个指标名称前面的 kb 前缀,表示这些指标的单位是 KB。

  • kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需 要内存的估计值。%commit,就是这个值相对总内存的百分比。
  • kbactive,表示活跃内存,也就是最近使用过的内存,一般不会被系统回收。
  • kbinact,表示非活跃内存,也就是不常访问的内存,有可能会被系统回收。

总的内存使用率(%memused)在不断增长,从开始的 23% 一直长到了 98%,并且主要 内存都被缓冲区(kbbuffers)占用。

剩余内存(kbmemfree)不断减少,而缓冲区(kbbuffers)则不断增大,剩余内存不断分配给了缓冲区。

一段时间后,剩余内存已经很小,而缓冲区占用了大部分内存。这时候,Swap 的使用开 始逐渐增大,缓冲区和剩余内存则只在小范围内波动。

关闭 Swap

swapoff -a

关闭 Swap 后再重新打开

swapoff -a && swapon -a

http://www.hskmm.com/?act=detail&tid=29590

相关文章:

  • 今天开通自己的博客啦,加油加油!成为合格的牛马! - Irving11
  • 2025安全光栅厂家最新权威推荐榜:精准防护与高效性能的工业
  • 分块学习笔记
  • 2025年10月精加工车间恒温恒湿系统厂家推荐:精准控温与高效节能首
  • 977. 有序数组的平方 双指针
  • 完整教程:iSCSI服务器
  • 深入解析:数据库视图:虚拟表的强大应用
  • agc001_c题解
  • 【IMU】6轴数据校准算法
  • 2025 年 MES 服务商 TOP 平台机构推荐排行榜,mes 系统 /mes 软件 /mes 制造执行系统 /mes 生产制造执行系统 /mes 生产管理系统公司推荐
  • 2025 年10月 WMS 服务商最新推荐榜单,wms系统 wms软件,wms仓库管理软件,wms仓库管理系统软件公司推荐
  • 【仿生机器人】核心采购清单 (仿生机器人头方案)
  • CF数据结构题做题记录-1
  • 完整教程:安宝特产品丨FME Realize:重构数据与现实的边界,让空间计算赋能现场决策
  • 尝试对音频功率放大器芯片的噪声基底特性进行测量与计算:以纳芯威NS4268为例
  • 3.1 策略梯度方法(Policy Gradient Methods)
  • perl语言中的三目运算符和do代码块
  • CCPC2023女生专场 游记(VP)
  • 2.5 分布式学习(Distributed Learning)
  • 心得:刷算法的痛点-只根据题目的case思考,不考虑边界情况,写出一坨shit
  • OI 数论 1
  • 2.4 DQN 变体(Rainbow)
  • Emacs折腾日记(三十二)——org mode的基本美化
  • 2025 工业风机十大品牌全景解析报告:覆盖离心风机,防爆风机,矿用风机的最新推荐
  • 2.3 深度 Q 网络(Deep Q-Network, DQN)
  • Linux存储媒介devmount
  • Linux系统目录(文件)结构
  • 实用指南:如何读懂Mach-O:构建macOS和iOS应用安全的第一道认知防线
  • vim配置使用
  • shell高级