首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux系统之 OOM 解析

Linux系统之 OOM 解析

作者头像
Luga Lee
发布于 2021-12-09 13:07:33
发布于 2021-12-09 13:07:33
3K00
代码可运行
举报
文章被收录于专栏:架构驿站架构驿站
运行总次数:0
代码可运行
在实际的业务场景中,有没有发现这样一种场景:基于 VM 环境上面所部署的 Spring Boot 应用服务,往往在运行过程中将内存利用的足够“猥琐”,常常达到 90% 甚至以上,此时,很大一部分伙伴就开始“叫”了。曰:领导,内存不够了,赶紧扩容!!!(此刻,有大佬肯定在想:扩你妹,整天搞这些没用的~)

那个傻子是不是疯了?不知道作为所谓的“技术”人员,大家是如何面对的,如何解决?本文将聚焦于 Linux 内存结构、内存分析以及 OOM killer 等 3 个方面以及笔者多年的实践经验总结进行“吹牛逼”,当然,若吹的不好,欢迎大家扔砖、鸡蛋。

内存结构

从宏观角度而言,内存管理系统是操作系统最重要的部分之一。在内存管理的系统调用方式,事实上,基于 POSIX 并没有给内存管理指定任何的系统调用。然而,Linux 却有自己的内存系统调用,主要系统调用如下:

系统调用

描述

s = brk(addr)

改变数据段大小

a = mmap(addr,len,prot,flags,fd,offset)

进行映射

s = unmap(addr,len)

取消映射

1、brk 通过给出超过数据段之外的第一个字节地址来指定数据段的大小。如果新的值要比原来的大,那么数据区会变得越来越大,反之会越来越小。

2、mmap 和 unmap 系统调用会控制映射文件。mmp 的第一个参数 addr 决定了文件映射的地址。它必须是页面大小的倍数。如果参数是 0,系统会分配地址并返回 a。第二个参数是长度,它告诉了需要映射多少字节。它也是页面大小的倍数。prot 决定了映射文件的保护位,保护位可以标记为 可读、可写、可执行或者这些的结合。第四个参数 flags 能够控制文件是私有的还是可读的以及 addr 是必须的还是只是进行提示。第五个参数 fd 是要映射的文件描述符。只有打开的文件是可以被映射的,因此如果想要进行文件映射,必须打开文件;最后一个参数 offset 会指示文件从什么时候开始,并不一定每次都要从零开始。

针对 Linux 内存管理及实现,其实其涉及的面较广,较为复杂,从计算机早期开始,我们在实际的业务场景中所使用的内存往往都要比系统中实际存在的内存多。为此,内存分配策略克服了这一限制,并且其中最有名的就是引入: 虚拟内存(Virtual Memory)。通过在多个竞争的进程之间共享虚拟内存,虚拟内存得以让系统有更多的内存,以方便维护系统资源的分配。先来张总概览图,具体如下所示:

(此图源自网络)

Linux 内存,通常被认为指的是“物理内存”,然而,只有内核才可以直接访问物理内存,进程需要访问内存,Linux 内核则需要为每个进程都提供一个独立的虚拟地址空间,访问的是虚拟内存。

通常而言,虚拟内存空间的内部被划分为内核空间和用户空间:

1、进程在用户态,只能访问用户空间内存

2、进程进入内核态才能访问内核空间内存

3、每个进程都包含内核空间,但这些内核空间都关联相同的物理内存

而针对内存映射,其主要将虚拟内存地址映射到物理内存地址,为了完成内存映射。内核每个进程都维护了一张页表,记录虚拟地址和物理地址的映射关系,页表实际存储在CPU 的内存管理单元 MMU,这样处理器就可以直接通过硬件找出要访问的内存。

再来一张内核线形地址空间布局图,具体可参考如下“硬核”示意图:

针对上述结构图,简单描述如下:

1、内核直接映射空间 PAGE_OFFSET~VMALLOC_START,kmalloc和__get_free_page()分配的是这里的页面。二者是借助 Slab分配器,直接分配物理页再转换为逻辑地址(物理地址连续)。适合分配小段内存。此区域 包含了内核镜像、物理页框表mem_map 等资源。

2、内核动态映射空间 VMALLOC_START~VMALLOC_END,被 vmalloc 用到,可表示的空间大。

3、内核永久映射空间 PKMAP_BASE ~ FIXADDR_START,kmap

4、内核临时映射空间 FIXADDR_START~FIXADDR_TOP,kmap_atomic

内存分析

针对内存分析部分,其实可利用的手段或策略较多,基于不同段位的水平高低之分,通常,我们可以 借助 Top、Free 命令以及 Vmstat 命令进行追踪及观测内存的动态活动变化趋势,以实时了解当前操作系统的资源水位,具体如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[administrator@JavaLangOutOfMemory ~ ] %top
 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  1 root      20   0  128032   7996   5556 S   80.0  80.4   0:01.03 java
  2 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kthreadd

基于上述输出结果,简要解析如下:

1、VIRI: 虚拟内存,包括了进程的代码段、数据段、共享内存、已经申请的堆内存和已经换出的内存等,已经申请的内存,即使还未分配物理内存,也算做虚拟内存

2、RSS: 常驻内存,是进程实际使用的物理内存,不包括 Swap 和共享内存

3、SHR: 共享内存,包括与其他进程共同使用的真实共享内存,包括加载的动态链接库以及程序的代码段

4、%MEM: 进程使用物理内存占系统内存的百分比

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[administrator@JavaLangOutOfMemory ~ ] %free
              total        used        free      shared  buff/cache   available
Mem:        2031744       98176     1826192        8784      107376     1800144
Swap:       2097148           0     2097148

此命令行输出内容较为简单:主要打印已用、剩余、可用、共享内存以及缓存等信息。部分参数释义如下所示:

1、Shared: 共享内存, 共享内存是通过 Tmpfs 实现的,它的大小就是 Tmpfs 使用的内存大小。

2、Available: 可用内存,是新进程可以使用的最大内存,包括剩余内存和还未使用的内存。

3、Buffer/Cache: 缓存包括两部分,一部分是磁盘读取文件的页缓存,用来缓存从磁盘读取的数据,加速以后再次访问速度,另一部分是 Slab 分配的可回收缓存;缓冲区是对原始磁盘的临时存储,用来缓存将要写入磁盘的数据,统一优化磁盘写入。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[administrator@JavaLangOutOfMemory ~ ] %vmstat 1 1 
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 1815348   2108 111872    0    0     1     0   11   11  0  0 100  0  0

基于上述输出结果,简要解析如下:

1、si: 换入,每秒从磁盘读入虚拟内存的大小,若此值长时间持续大于0,表示物理内存不够或者内存泄漏,需要定位问题。

2、so: 换出,每秒从内存写入磁盘的大小,若此值长时间持续大于0,表示物理内存不够用,需要排查内存问题。

OOM Killer

通常有这样的一种场景:若一台 VM (虚拟机)上部署多个应用服务,此处,暂以 Spring Boot 微服务为例,在某种特殊的时刻,例如:业务促销、压力测试或当某一个联机负载节点或因网络抖动而挂掉时,此台 VM 上的服务突然在毫无征兆的情况下,突然被“挂掉”。

与此同时,我们开始搜集相关线索,以便能够快速定位到问题原因,将“罪魁祸首”抓捕归案。

那么,为什么会出现这种问题?它是如何产生的?OOM,全称为 “Out Of Memory”,即 内存溢出。OOM Killer 是 Linux 自我保护的方式,防止内存不足时出现严重问题。

Linux 内核所采用的此种机制会时不时监控所运行中占用内存过大的进程,尤其针对在某一种瞬间场景下占用内存较快的进程,为了防止操作系统内存耗尽而不得不自动将此进程 Kill 掉。通常,系统内核检测到系统内存不足时,筛选并终止某个进程的过程可以参考内核源代码:linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory()被触发,然后调用 select_bad_process() 选择一个 ”bad” 进程杀掉。如何判断和选择一个”bad 进程呢?Linux 操作系统选择”bad”进程是通过调用 oom_badness(),挑选的算法和想法都很简单很朴实:最 bad 的那个进程就是那个最占用内存的进程。

OOM Killer 源码解析

OOM killer的核心函数是 out_of_memory(), 执行流程如下:

1、调用 check_panic_on_oom() 检查是否允许执行内核恐慌,假如允许,需要重启系统。

2、若定义了 /proc/sys/vm/oom_kill_allocating_task 即允许 Kill 掉当前正在申请分配物理内存的进程,那么杀死当前进程。

3、调用 select_bad_process,选择 badness score 最高的进程。

4、调用 oom_kill_process, 杀死选择的进程。

我们通过分析 Badness Score 的计算函数来理解 OOM Killer 是如何选择需要被 Kill 掉的进程,具体源代码可参考如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
unsigned long oom_badness(struct task_struct *p, struct mem_cgroup *memcg,
                          const nodemask_t *nodemask, unsigned long totalpages)
{
        long points;
        long adj;

        /* 假如该进程不能被kill, 则分数返回0. */
        if (oom_unkillable_task(p, memcg, nodemask))
                return 0;

        p = find_lock_task_mm(p);
        if (!p)
                return 0;

        /* 获取该进程的 oom_score_adj, 这个是用户为进程设置的 badness score
         * 调整值,假如这个值为-1000或者进程被标记为不可被kill,或者进程处于
         * vfork()过程,badness score返回0. */
        adj = (long)p->signal->oom_score_adj;
        if (adj == OOM_SCORE_ADJ_MIN ||
                        test_bit(MMF_OOM_SKIP, &p->mm->flags) ||
                        in_vfork(p)) {
                task_unlock(p);
                return 0;
        }

        /* badness score分数 = 物理内存页数 + 交换区页数 + 页表Page Table数量. */
        points = get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS) +
                mm_pgtables_bytes(p->mm) / PAGE_SIZE;
        task_unlock(p);

        /* 利用以下公式对 badness score 值进行调整. */
        adj *= totalpages / 1000;
        points += adj;

        /* 返回 badness score, 假如等于0, 则返回 1. */
        return points > 0 ? points : 1;
}

通过对 Badness Score 计算函数的分析,我们可以发现 OOM Killer 是基于 RSS 即常驻的物理内存来选择进程进行 Kill 操作, 从而释放相关内存以进行系统自我保护。有关 OOM Killer 相关配置、查看及分析将于后续文章给出,大家到时留意查看。

综上所述,本篇文章主要通过基于对 Linux 内存结构、分析及 OOM Killer 3个核心维度,从主动及被动场景等 2 方面对 Linux 操作系统内存的剖析,以探讨在实际的业务场景中,内存表现的相关活动及经验认知。 至此,关于 Linux 系统内存解析相关内容本文到此为止,大家有什么疑问、想法及建议,欢迎留言沟通。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 架构驿站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
细说|Linux Out Of Memory机制
有时候我们会发现系统中某个进程会突然挂掉,通过查看系统日志发现是由于 OOM机制 导致进程被杀掉。
用户7686797
2023/02/26
3.4K0
细说|Linux Out Of Memory机制
Linux OOM机制分析
oom_killer(out of memory killer)是Linux内核的一种内存管理机制,在系统可用内存较少的情况下,内核为保证系统还能够继续运行下去,会选择杀掉一些进程释放掉一些内存。通常oom_killer的触发流程是:进程A想要分配物理内存(通常是当进程真正去读写一块内核已经“分配”给它的内存)->触发缺页异常->内核去分配物理内存->物理内存不够了,触发OOM。
ivanren
2018/07/06
8.6K0
五万字 | 深入理解Linux内存管理
作者简介: 程磊,一线码农,在某手机公司担任系统开发工程师,日常喜欢研究内核基本原理。 1.1 内存管理的意义 1.2 原始内存管理 1.3 分段内存管理 1.4 分页内存管理 1.5 内存管理的目标 1.6 Linux内存管理体系 2.1 物理内存节点 2.2 物理内存区域 2.3 物理内存页面 2.4 物理内存模型 2.5 三级区划关系 3.1 Buddy System 3.1.1 伙伴系统的内存来源 3.1.2 伙伴系统的管理数据结构 3.1.3 伙伴系统的算法逻辑 3.1.4 伙伴系统的接口 3.1
刘盼
2022/08/26
4.4K0
五万字 | 深入理解Linux内存管理
深入了解Linux OOM Killer:一次可怕的内核事件
The OOM Killer 是内核中的一个进程,当系统出现严重内存不足时,它就会启用自己的算法去选择某一个进程并杀掉. 之所以会发生这种情况,是因为Linux内核在给某个进程分配内存时,会比进程申请的内存多分配一些. 这是为了保证进程在真正使用的时候有足够的内存,因为进程在申请内存后并不一定立即使用,当真正使用的时候,可能部分内存已经被回收了。
嵌入式Linux内核
2023/08/08
6.2K0
深入了解Linux OOM Killer:一次可怕的内核事件
OOM Killer的一点分析
最近线上遇到了好几次由于内存泄漏导致OOM的问题,且大部分都是整个模块被kill掉woker进程,只剩下接入的epoll进程和统计进程的情况,从而导致拨测程序在没有做逻辑拨测的情况下,不会重新拉起程序,导致机器无法服务。
暴雨一场
2018/07/28
2.2K0
郭健:Linux内存管理系统参数配置之OOM(内存耗尽)
本文是描述Linux virtual memory运行参数的第二篇,主要是讲OOM相关的参数的。为了理解OOM参数,第二章简单的描述什么是OOM。如果这个名词对你毫无压力,你可以直接进入第三章,这一章是描述具体的参数的,除了描述具体的参数,我们引用了一些具体的内核代码,本文的代码来自4.0内核,如果有兴趣,可以结合代码阅读,为了缩减篇幅,文章中的代码都是删减版本的。按照惯例,最后一章是参考文献,本文的参考文献都是来自linux内核的Documentation目录,该目录下有大量的文档可以参考,每一篇都值得细细品味。
Linux阅码场
2019/10/08
2.9K0
郭健:Linux内存管理系统参数配置之OOM(内存耗尽)
cgroup oom引发Pod重建问题分析
业务在上容器云的过程中发现容器不知原因被重建,查看message信息可以看到当 oom_score_adj配置为1,对应score值为0的进程杀完后如果系统还是触发oom时就开始杀pause进程。
cdh
2020/06/02
3.4K1
记录一次应用被突然kill掉的问题定位经历
问题背景:一次启动本地应用,两分钟过后自动退出,通过日志并未发现任何异常状况,莫名其妙的应用就自动被杀掉了;
司夜
2021/07/07
2.5K0
记录一次应用被突然kill掉的问题定位经历
Linux内存描述之高端内存--Linux内存管理(五)
过去,CPU的地址总线只有32位, 32的地址总线无论是从逻辑上还是从物理上都只能描述4G的地址空间(232=4Gbit),在物理上理论上最多拥有4G内存(除了IO地址空间,实际内存容量小于4G),逻辑空间也只能描述4G的线性地址空间。
233333
2018/12/19
13.9K0
Android 进程回收之LowMemoryKiller原理篇
在前面的文章Android进程保活一文中,对于LowMemoryKiller的概念做了简单的提及。LowMemoryKiller简称低内存杀死机制。简单来说,LowMemoryKiller(低内存杀手)是Andorid基于oomKiller原理所扩展的一个多层次oomKiller,OOMkiller(Out Of Memory Killer)是在Linux系统无法分配新内存的时候,选择性杀掉进程,到oom的时候,系统可能已经不太稳定,而LowMemoryKiller是一种根据内存阈值级别触发的内存回收的机制
xiangzhihong
2018/01/26
3.8K0
oom killer
Linux系统内存管理中存在着一个称之为OOM killer(Out-Of-Memory killer)的机制,该机制主要用于内存监控,监控进程的内存使用量,当系统的内存耗尽时,其将根据算法选择性地kill了部分进程。本文分析的内存溢出保护机制,也就是OOM killer机制了。
233333
2019/05/25
2K0
77%的Linux运维都不懂的内核问题
来源:高效运维 ID:greatops 前言 之前在实习时,听了 OOM 的分享之后,就对 Linux 内核内存管理充满兴趣,但是这块知识非常庞大,没有一定积累,不敢写下,担心误人子弟,所以经过一个一段时间的积累,对内核内存有一定了解之后,今天才写下这篇博客,记录以及分享。 【OOM - Out of Memory】内存溢出 内存溢出的解决办法: 1、等比例缩小图片 2、对图片采用软引用,及时进行 recycle( ) 操作。 3、使用加载图片框架处理图片,如专业处理图片的 ImageLoader 图片加
小小科
2018/06/20
2.1K0
软件性能测试(连载9)
Linux内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的。Linux的空间又分为内核空间和用户空间,在32位中,内核空间占1G,用户空间占3G;而在64位中,内核空间和用户空间各占128T。如图3-24所示。
顾翔
2020/02/20
1K0
Redis在Linux系统的配置优化
通常来看,Redis开发和运维人员更加关注的是Redis本身的一些配置优化,例如AOF和RDB的配置优化、数据结构的配置优化等,但是对于操作系统是否需要针对Redis做一些配置优化不甚了解或者不太关心,然而事实证明一个良好的系统操作配置能够为Redis服务良好运行保驾护航。
程序猿DD
2020/11/09
2.4K0
Redis在Linux系统的配置优化
Linux系统面试题
用户空间(User Space) :用户空间又包括用户的应用程序(User Applications)、C 库(C Library) 。
thierryzhou
2022/12/01
1.8K1
Linux系统面试题
程序OOM后,还能正常访问吗?
今天要探讨的是最近不知道为什么突然间火起来的面试题:当JAVA程序出现OOM之后,程序还能正常被访问吗?答案是可以的,很多时候他并不会直接导致程序崩溃,而是JVM会抛出一个error,告知你程序内存溢出了。当然也要分操作系统。
有一只柴犬
2024/05/26
5470
程序OOM后,还能正常访问吗?
记一次生产服务器进程突然消失问题排查!
这件事是真实的发送在我们的生产环境上,其中的一台服务器上跑着 4 个 jar 程序,隔三差五的会发送进程突然消失的问题。
业余草
2020/09/22
2.4K0
【Android开发高级系列】内存管理专题
        在Android系统中,进程可以大致分为系统进程和应用进程两大类。
江中散人_Jun
2023/10/16
4500
【Android开发高级系列】内存管理专题
如何使用 BPF 监控 Linux 内存情况:Linux 内存调优之 BPF 内存监控分析
动态跟踪点: kprobes 和 uprobes,类似于内核态和用户态的方法埋点,比较灵活,可以任意监控
山河已无恙
2025/07/08
1440
如何使用 BPF 监控 Linux 内存情况:Linux 内存调优之 BPF 内存监控分析
腾讯一面:内存满了,会发生什么?
先来说说第一个问题:虚拟内存有什么作用?(如果你还不知道虚拟内存概念,可以看这篇:真棒!20 张图揭开内存管理的迷雾,瞬间豁然开朗)
小林coding
2022/10/27
1.4K0
腾讯一面:内存满了,会发生什么?
相关推荐
细说|Linux Out Of Memory机制
更多 >
交个朋友
加入HAI高性能应用服务器交流群
探索HAI应用新境界 共享实践心得
加入[游戏服务器] 腾讯云官方交流站
游戏服运维小技巧 常见问题齐排查
加入架构与运维学习入门群
系统架构设计入门 运维体系构建指南
换一批
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档