news 2026/9/30 3:12:48

Linux内存管理实战:从虚拟内存到OOM Killer排查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内存管理实战:从虚拟内存到OOM Killer排查指南

刚接触Linux的同学,八成遇到过这些经典场面:服务器跑了一阵子,用free -h一看内存被“吃”得干干净净;一个逻辑并不复杂的程序,跑起来却被OOM Killer当场击杀;top里某个进程的RES动不动就占几个GB,可业务数据明明没这么大。这些问题绕来绕去,最后都会指向同一个方向——Linux内存管理。

市面上讲Linux内存管理的资料很多,但往往不是太偏理论就是太散,初学者很容易迷失在“页表、缺页、伙伴系统、slab、swap”这些名词里。这篇文章我想换个方式,从实际现象和操作入手,把内存管理最核心的知识点串成一条线,让零基础的朋友也能看懂,并且看完就能用命令行去排查一些真实问题。无论你是刚入行的运维、转语言的嵌入式开发者,还是日常把Linux当主力系统使用的用户,这篇内容都能帮你在脑中搭起一张地图。

1. 为什么初学者要理解Linux内存管理

1.1 从三个常见现象说起

先看第一个现象:一台新装的Linux服务器,开机后free -h显示used只有几百兆,可运行几天之后,used那一栏涨到了几个G,连buff/cache都占得满满当当。很多新手第一反应是“中病毒了”或者“内存泄漏了”,实际上大部分情况下这是Linux的正常行为。它把空闲物理内存拿去做文件缓存,用来加速磁盘读写。说白了,Linux觉得内存闲着也是闲着,不如当做缓存用。这是理解内存管理的第一步:used高不等于有问题,cache高更不等于有问题。

第二个现象:一个Java或Python程序,设置的内存上限明明不大,可系统监控里它的RSS(驻留内存)数值高得离谱,甚至比代码里分配的总量还大。很多人不理解虚拟内存和物理内存的区别,看到VIRT和RES就慌了。其实一个程序占了多少物理内存,要看RES;它“看起来”占了多大地址空间,则要看VIRT。这个区别贯穿整个内存管理的学习。

第三个现象:服务器负载不高,内存还有富余,但系统却开始使用swap,磁盘狂转,程序卡得不能自理。这个问题涉及内核在什么条件下决定回收页面、什么时候把匿名页换出到swap,也涉及一个非常有名的参数vm.swappiness。不懂背后的机制就乱调参数,很容易把系统调得更糟。

这三个现象背后的问题,本质就是Linux内存管理的三个大主题:地址空间与分页、页面分配与回收、以及我们在用户态能做的观测与干预。

1.2 内存管理中你和内核的分工

学习内存管理前,先建立一个总体的“分工观”:应用程序负责在用户态申请内存、使用内存;内核负责分配物理内存、维护地址映射、在内存紧张时决定回收哪些页面。这两层不是各自为政的,中间靠系统调用(比如mmap、brk)和缺页异常机制来衔接。

很多初学者容易犯的毛病,是试图在用户态“手动管理”内存,到处调用malloc和free,却不理解底层的行为。比如分配了内存但从未写入,那一部分其实并没有真正占用物理内存;又比如free掉一块内存后,地址空间里的映射还在,只是变得不再有意义。这些细节都会影响你定位问题时的判断。

从这个角度看,内存管理的学习路径应该自上而下:先搞懂我们看到的进程地址空间是什么,再往下理解页表怎么把虚拟地址翻译成物理地址,接着看内核如何分配和回收页面,最后用命令把整个状态读出来。下面的章节就按这个顺序展开。

2. 内存管理的核心概念

2.1 虚拟内存:让每个进程以为自己在独占内存

虚拟内存(Virtual Memory)是理解整个Linux内存管理的第一块基石。简单说,每个进程都被内核“骗”了:它以为自己有一整块从0开始、连续且独享的地址空间,实际上这些地址是虚拟的,并不直接对应物理内存条上的某个位置。

为什么内核要绕这么一圈?直接让进程用物理地址不好吗?答案很简单:隔离和安全。如果没有虚拟内存,进程A可以轻松读写进程B的内存,系统毫无安全性可言;同时,物理内存的碎片化也会让“连续分配一大块内存”变成噩梦。虚拟内存把这些复杂性全部挡在硬件和内核身后。

每个进程的虚拟地址空间通常分为几个区域:代码段、数据段、堆(heap)、内存映射区、栈(stack),以及内核空间区域。用cat /proc/self/maps可以看到当前进程完整的地址空间布局。我第一次看到这个文件时很震撼,因为一行行地址区间非常规整,每个区间都标注了权限r-xp、rw-p等对应关系。看懂这个文件,几乎等于看懂了进程对内存的一切需求。

需要注意的是,虚拟地址空间只是“地图”,地图上标注的地址不一定有实际物理页支撑。只有进程真正访问某个虚拟地址时,内核才会把对应的物理页面分配好,并且把映射关系填到页表里。这种“惰性分配”设计非常巧妙,它让系统可以承诺比物理内存大得多的地址空间,也避免了一申请内存就立即占满物理内存的浪费。

2.2 页面、页表与TLB:地址翻译的三件套

虚拟地址和物理地址之间不会是一对一直接换算的,而是以“页”为粒度进行管理。在Linux的典型配置下,物理内存被划分成固定大小的页框,最常见的是4KB大小。虚拟地址同样按4KB切分,所以一次映射操作的单元就是“一页”。

页表(Page Table)就是记录虚拟页到物理页映射关系的“账本”。进程每次访问内存时,CPU都会使用该进程的页表,把虚拟地址翻译成物理地址,然后才真正访问内存颗粒。这里有个很现实的问题:如果每次都从内存里读页表,速度会慢得难以接受。因此现代CPU引入了TLB(Translation Lookaside Buffer),也就是页表项的缓存。TLB命中时,地址翻译几乎零成本;一旦TLB未命中,就要走完整的多级页表查询流程,性能开销明显上升。

这也是为什么大页面(HugePage)在很多高性能场景那么重要的原因:页面变大以后,同样大小的物理内存区域对应的页表项数量大幅减少,TLB的覆盖率更高,翻译成本更低。比如默认4KB页面下,2MB大页可以让TLB覆盖的内存范围扩大512倍。对数据库这类内存访问密集的应用,开启透明大页或者显式使用大页,常常能看到可感知的性能变化。

但页表不是越小越好的简单问题。为保证效率,Linux用的是多级页表结构,在64位体系结构下通常是四层或五层。多级结构的好处是:进程的虚拟地址空间再大,大部分层级其实都是空的,不需要分配物理页来存储这些空目录。每当进程需要一个新页时,内核才逐级建立目录项,这种按需建表的方式大大节省了页表自身占用的内存。

2.3 缺页异常(Page Fault)不是错误

初学Linux时,一听到“缺页异常”就以为是程序出错,实际上这是内存管理系统最核心的机制之一,也是虚拟内存能工作起来的“引擎”。当进程访问一个虚拟地址,而页表里没有对应有效映射时,CPU会触发缺页异常,进入内核的缺页处理流程。

这里需要区分两种情形:一是映射存在但物理页尚未分配,这叫“轻微缺页”或“软缺页”,内核马上分配一个零页并建立映射,然后返回用户态继续执行,进程本人几乎无感知;二是映射本身就不存在,比如访问了空指针或者越界地址,这叫“严重缺页”或者叫段错误,内核直接向进程发送SIGSEGV信号,于是你看到“Segmentation fault”并退出。

在写程序时,有一种经典误区:malloc一大块内存之后,立刻检查返回值,以为内存已经分配成功了。其实malloc成功只代表虚拟地址空间分配成功,真正分配物理页要等你开始写这块内存时才会一个一个发生。用strace跟踪一个简单C程序,你会发现malloc底层调用的是brk或mmap,但这两者都不会在瞬间把物理内存全部预备好。真正的物理内存分配,藏在那一次次看似普通的缺页处理里。

可以用一个小实验来验证:写一个程序申请1GB内存但不触碰它,再用top观察RES,会发现它非常小;一旦逐页写入,RES立刻涨上去。这个实验直观地揭示了“虚拟内存”和“物理内存”之间的巨大区别,也解释了为什么一个进程的VIRT可以轻松超过物理内存总量。

2.4 mmap与文件映射:为什么文件操作这么快

在Linux中,除了匿名内存映射,还有一种非常重要的映射类型:文件映射。mmap能把一个文件的一部分或全部直接映射到进程的虚拟地址空间。映射完成后,进程读写这段内存,就等于读写文件,内核会在后台处理具体的磁盘IO。这就是内存映射I/O的基本模型,也是很多高性能组件跑得快的原因之一。

文件映射带来的一个有趣效果,是多个进程映射同一个文件时,物理页可以被共享。比如你用mmap把同一个共享库映射到三个进程里,内核只需在物理内存里保存一份库代码页,三个进程的页表各自指向同一份物理页。这种共享极大减少了内存占用。你可以用top里的SHR字段观察一个进程的共享内存量,那通常就是共享库、共享内存段等带来的“福利”。

理解文件映射之后,你就能明白Linux的页缓存(Page Cache)为什么存在:当一个文件被读取时,内核会尽量把页缓存留在物理内存里,而不是立刻丢弃。下次再读同一个文件,就可以直接从内存命中,不用动磁盘。这和Windows下那种“内存占用高就慌”的思路很不一样,Linux倾向于“尽量用内存,不够再说”。后续我们看free命令时,会再次碰到buff/cache这一项,它就是这套机制的宏观体现。

3. 内核里的分配器与回收机制

3.1 从malloc到内核:一条需要知道的调用链

应用层内存分配看似简单:C语言的malloc,C++的new,Java的堆内分配。但在Linux内部,用户态分配器与内核之间的分工十分微妙。

对C程序来说,glibc的malloc是用户态分配器,它维护着一堆空闲块,用来减少频繁进入内核的开销。malloc实现时会根据申请大小分两条路走:小内存通过brk系统调用调整程序堆的边界,大内存(通常超过M_MMAP_THRESHOLD,默认128KB)则通过mmap创建一块匿名映射。到了内核这一层,真正负责物理页面分配的是伙伴系统和slab分配器。

很多性能问题出在这一层:用户态分配器为了效率,不一定把free的内存立即还给内核。你free了一大块内存,但top里这个进程的RSS可能一点没降,因为分配器把这些块保留在自己的池子里,后面可以复用它。于是有人误以为“内存泄漏了”。这种“占着不还”的行为不一定是坏事,但如果你在写非常长时间运行的守护进程,且内存分配释放模式不断变化,就需要关注碎片的积累。

理解这条调用链有什么实际意义?排查问题时,如果怀疑是内存泄漏,第一件事不是看代码,而是确认内存是“确实被业务消耗”,还是“分配器缓存未释放”,或是“真的泄漏”。三者的处理方式完全不同。直接free掉地址空间并不总能立竿见影地降低RSS,因为要和线程局部缓存、分配器衰减策略等机制一起看。

3.2 伙伴系统与slab:内核自己的内存“超市”

内核管理物理内存的主分配器叫伙伴系统(Buddy System)。它的核心思想是把物理页按2的幂次分成不同大小的块,例如1页、2页、4页、8页……当内核需要连续的若干页时,就从对应大小链表中取出一块,如果找不到足够大的块,就把大块一级一级拆分。释放时则尝试把相邻的空闲块合并成更大的块。

这套算法的好处是分配和释放速度快,且能有效减少外部碎片。代价是内部碎片——比如内核只要3页,系统却分配了4页的块,多出的1页就浪费了。所以伙伴系统主要面向“按页为单位”的物理内存需求。

内核自己的数据结构怎么办?如果每次创建一个进程控制块都直接向伙伴系统申请一页内存,那太浪费了,因为一个结构体可能只需要几百字节。为此Linux又引入了slab分配器。slab从伙伴系统那儿批量申请一些页,把它们切成大小固定的对象缓存起来,内核需要task_struct时就从一个缓存里拿,用完了再放回去。这样既减少了对伙伴系统的频繁打扰,也避免了反复初始化的开销。

对普通开发者和运维来说,slab的可见出口是/proc/slabinfo,以及slabtop命令。如果遇到“内核内存泄漏”这类高级问题,slabtop能帮你快速看到哪个内核对象的数量异常增长。比如曾有一些老版本内核因为网络套接字缓存未释放,导致slab内存持续飙高,slabtop里能看到kmalloc-*或socket_inode_cache异常。这种问题从用户态进程的RSS里根本看不出端倪。

3.3 页面回收、swap与OOM Killer

物理内存永远是有限资源,所以内核必须有一套“内存不够时谁先走”的策略。页面回收(Page Reclaim)机制会周期性或按需地扫描页面,把不常用的页收回。回收分两类:一种是回收页缓存,也就是把文件页占的内存丢掉,数据还在磁盘上,下次读时再从磁盘拉;另一种是回收匿名页,那就必须先把数据写到swap分区或者swap文件里,否则一丢就真没了。

swap不是Llinux的“备胎”,而是一种扩展和保险机制。它的存在让系统能容纳超出物理内存的工作负载,但也引入了磁盘IO的延迟。如果机器频繁读写swap,通常意味着物理内存真的吃紧,这时候先别急着骂内核,应该冷静查看到底是什么进程占用过大,或者是不是参数配置过于激进。

OOM Killer(Out Of Memory Killer)是内核里的“最后手段”。当系统物理内存乃至swap都严重耗尽,内核必须挑一个进程杀掉来释放内存。很多初学者看到日志里出现Out of memory: Kill process就慌得不行,理解它的触发逻辑比害怕它更重要。内核为每个进程维护一个oom_score,分数越高越容易被杀。默认逻辑倾向于杀“内存占用大但存活时间短”的进程,所以长期运行的数据库或Java服务,得分通常被压低,反而新启动的短命进程更容易被杀。

被OOM杀掉之后,第一件事不是重启服务,而是查看内核日志里被选中的进程和当时的内存状况,还要看是不是某个瞬间的尖峰导致。如果服务反复被杀,与其调参数,不如先查代码或配置里有没有内存不受控的地方。

4. 用命令行观测Linux内存

4.1 free -h:最基础的一屏数字

没有任何花哨操作,登录一台Linux机器后,我通常第一个敲的是free -h。输出看起来很简单,但每一列都有讲究。

以现在的内存大小为例,你会看到total、used、free、shared、buff/cache和available这几列。其中available是最值得关注的一个值,它表示在不触发swap的前提下,估算还能给新程序分配多少内存。为什么不能只看free列?因为free只算完全空闲的物理页,而buff/cache里的一部分页是可以被内核临时“征用”的,available把这部分可回收页也估算进去了,所以数值通常会比free大得多。

我在实际排障时发现,很多新手误以为used高就等于内存不够,其实used里并不包含page cache那部分,但它在统计时把内核自身使用的内存也算进去了。要看“内存是否紧张”,请把目光聚焦在available上,再配合vmstat里的si和so,基本就能判断系统的真实状态。

4.2 /proc/meminfo:把内核内存状态摊开来看

free命令底层的实时数据源其实是/proc/meminfo。如果你想更细致地理解系统内存,直接查看这个文件是必修课。它有很多字段,但初学者不需要全部记住,先认识几个关键的即可。

MemTotal代表总物理内存大小;MemFree是未使用的物理页;MemAvailable就是free命令里的available估计值来源;Buffers对应块设备或文件系统元数据缓存;Cached大部分是页缓存,也就是文件内容缓存;SwapTotal和SwapFree则反映swap空间总量和剩余量。

我自己排查内存问题时,最常看的是MemAvailable和Cached。如果MemAvailable持续走低,且swap数值在增长,说明确实有压力;如果MemAvailable稳定但Cached很大,那是系统在用空闲内存做文件缓存,大概率没有问题。另外,/proc/meminfo里还有DirectMap字段,它告诉你物理内存中有多少是通过大页直接映射的。在两个大内存机器上,这个数值相对越大,TLB的表现通常就越好。

4.3 vmstat与top:动态观测内存变化

静态的一屏数据只能告诉你“某一时刻的状态”,动态观测要配合vmstat和top。vmstat 1会每隔一秒刷新一次输出,重点关注r(运行队列)、free(空闲内存)、si(从swap换入)、so(换出到swap)。si和so长期不为0的时候,系统大概率正在频繁交换,性能会明显下降,这时候压测或业务高峰就要格外小心。

top命令虽然人人会用,但关注点值得重新梳理。第一行的负载和CPU使用情况先放一边,只看内存相关项目:VIRT是虚拟地址空间总大小,RES才是常驻物理内存,SHR是共享内存,S是进程状态。判断一个进程的真实内存占用,请以RES为基准,不要被VIRT吓到。很多资深工程师看一个进程是否内存异常,还会按RES列排个序,然后逐个确认行为是否正常。

如果你觉得top交互刷新不够友好,可以加-b -n 2做批量输出,或者用top -o %MEM按内存占用排序。我自己在写脚本巡检时,更常用的其实是ps aux --sort=-%mem,因为它更容易被脚本解析。

4.4 实战:定位一个“内存占用异常”的进程

假设有人在工单里说“服务内存涨上去了,是不是泄漏了”。我的排查顺序大致如下:

第一,用free -h确认系统整体状态,看available是否在持续下降;第二,用ps aux --sort=-%mem找出RES最大的几个进程,确认到底哪个进程在涨;第三,用top -H -p <pid>看这个进程内部的线程分布,找出具体线程;第四,进/proc/<pid>目录查看更详细的信息,比如/proc/<pid>/status里的VmRSS、VmPeak、VmSwap,以确认进程是否已经发生换页。

如果到这里仍无法判断,会再看/proc/<pid>/smaps,这个文件按虚拟内存区域的粒度列出了每块映射的RSS大小。之前有位同事写的程序把一整个超大日志文件用mmap映射了,但代码逻辑里有个指针偶然越界,导致内核反复把大块文件页加载到内存,smaps里对应区域RSS高得惊人。这种问题只从进程总RSS看不透明,看smaps才一目了然。

5. 常见问题与排查技巧实录

5.1 为什么内存还有剩余,系统却开始用swap

这个问题的答案要先看内核的一个参数:vm.swappiness。它的取值范围是0到100,默认通常是60,含义是内核“倾向于使用匿名页回收和交换”的程度(值越高,越倾向把不常用的匿名页换出来)。但注意,这个参数不等于“内存用掉百分之多少才开始swap”,而是控制内核在页面回收时对“页缓存回收”和“匿名页回收”的偏好权重。

即使available还有不少,如果存在大量长时间未被访问的匿名页,内核也可能把它们换出,以腾出内存给页缓存。这在某些场景下会造成“性能反而变差”的错觉。比如一个交互率很低的Spark作业,空闲的堆内存页面被swap出去,下次再用时又换入,白白增加IO。

遇到这种情况,我建议先冷静观察两三天,看看si/so的持续程度。如果只是偶尔发生,没必要调整;如果持续发生,且通过分析确认程序确实需要这些内存,再考虑调低vm.swappiness。不过单纯调低这个参数并不总能解决问题,根本方法要么是给程序合理的内存限制,要么是增加物理内存或优化程序的内存占用结构。

5.2 如何识别内存泄漏

很多人一提内存排查就默认是内存泄漏,但真实世界里有大量“内存上涨”并不是泄漏,而是缓存、线程栈、池化资源、分配器未释放等共同作用的结果。识别是否是泄漏,核心看点在于:内存是否无上限地持续增长,且增长速率跟业务请求量没有固定比例。

一个可用的方法是使用smem或者周期抓取/proc/<pid>/status中的VmLib、VmRSS、VmSwap,做成时间序列曲线。如果曲线呈阶梯上升,且程序执行完“理应释放资源”的业务流程后内存仍不回落,就要高度怀疑泄漏了。接着用valgrind --leak-check=full跑C/C++程序,或对Java进程使用jmap结合MAT工具分析堆转储。

有一次我定位一个Go服务的内存不断上涨,用top怎么盯都看不出异常,后来抓goroutine数量,发现问题是因为每次请求都会泄漏一个定时器,旧定时器无法被GC回收,堆内存自然越堆越高。这类问题如果只停留在看RSS,很容易误判成“内核对内存的管理有问题”。

5.3 OOM Killer的日志怎么看

当系统触发OOM时,dmesg或journalctl -k里会留下一段足够清晰的日志。里面会有Out of memory: Kill process <pid> (<name>) score <score> or sacrifice child之类的内容。初学者看到这行字,往往只记住自己被杀了哪个进程,却不看背后的Mem-Info信息。

那段长日志里的Node 0、free、file、anon、slab等数据,才是判断OOM根因的重要线索。你需要关注几点:当时free内存是不是真的到了极低值;anon(匿名页)占总内存的比例;shmem是否很大;以及有没有进程在短时间内快速吞噬内存的迹象。如果free还剩下不少但依然触发OOM,通常与cgroup的内存限制有关,日志里还会给出对应的内存控制器详细信息。

被OOM杀死的服务重启之后,我建议立刻采集当时的监控图,特别是内存使用率的分钟级曲线,判断是瞬时尖峰还是持续爬坡。如果是瞬时尖峰,可能来自突发大请求或并发连接暴涨;如果是持续爬坡,那就是渐进式泄漏或缓存膨胀。

5.4 free命令显示的缓存要不要手动清理

网上流传一种“土办法”:内存不够了,就执行echo 3 > /proc/sys/vm/drop_caches清缓存。这种做法在特定场景下有它的合理性,比如你马上要做基准测试,希望排除页缓存干扰,或者需要验证冷启动性能。但对日常工作来说,我强烈不建议动不动就清缓存。

为什么?因为页缓存本来就是在那里提高命中的,你清了它,下次读同样的磁盘文件时反而要重新从慢速磁盘加载,整体性能只会更差。而且drop_caches只清可回收的缓存,不会释放进程的RSS,那些真正占用内存的程序并不会因此得到好处。很多新手清完缓存再看free,以为内存“省”下来了,其实只是把系统的加速能力丢掉了。

正确的处理方式,是先判断内存紧张是真紧张还是虚紧张。如果MemAvailable长期稳定、swap没有大的波动,缓存该留就留;如果确实紧张,先找异常进程,再考虑限制进程内存或调整服务配置,而不是急着清缓存。

6. 调优与学习路线的几点心得

6.1 sysctl参数不是用来乱调的

/proc/sys/vm/下面有一堆可以实时调整的内存相关参数,比如swappiness、min_free_kbytes、overcommit_memory、overcommit_ratio。每次看网上文章推荐“优化”意见时,我都建议先弄清楚这个参数到底在调控哪个环节,再做小范围实验,而不是照单全收。

以overcommit_memory为例,它控制内核是否允许用户进程申请超过“物理内存+swap”总额的虚拟内存。默认值0代表启发式overcommit,内核根据当前空闲情况做判断;值1代表总是允许overcommit;值2代表禁止超过一定比例的overcommit。对于普通服务器来说,默认0通常是最合适的。把值改成1可能让程序申请到大量虚拟地址空间,但等它真正写内存时才会触发物理内存压力;改成2则可能让一些依赖大地址空间的应用直接申请失败。

调优唯一正确的路径是:先在测试环境验证,再上生产;每次只调一个参数,并用监控数据前后对比。

6.2 容器与cgroup场景下的内存视角

现在大量应用跑在容器里,光看主机的free已经远远不够。容器的内存隔离主要由cgroup实现,限制写到/sys/fs/cgroup/memory/memory.limit_in_bytes(旧版cgroup v1)或/sys/fs/cgroup/<会话id>/memory.max(cgroup v2)。当容器内进程的内存使用达到cgroup限制时,即使宿主机还有大量空闲内存,容器内的进程也可能被OOM Killer杀死,这也是为什么“宿主机内存明明充足,容器却报OOM”的原因。

排查容器内存问题时,建议先看容器内的/proc/meminfo,再看对应的cgroup下的memory.current和memory.events,注意oom_kill的计数在不在涨。如果memory.stat里的anon和file分布异常,也能看出是匿名内存占大头还是页缓存占大头。这些经验和传统物理服务器排障的思路有区别,千万别把两者混为一谈。

6.3 给初学者的几条学习建议

第一篇内存管理文章如果光看理论会非常枯燥,我建议用一小块时间搭一台虚拟机,或者直接在你手头的Linux机器上做几个实验。第一个实验是:写一个循环分配内存并写入的程序,观察RSS的变化曲线;第二个实验:用mmap映射一个大文件,对比读它前后的free缓存变化;第三个实验:调低cgroup内存限制,观察容器被杀的全过程。亲手触发一次OOM Killer和看十篇博客的收获完全不在一个量级。

对于想进一步深入的人,我比较推荐这几条路径:第一,读man proc里和内存相关的字段说明,内核文档比二手资料靠谱得多;第二,理解CPU的TLB和页表缓存机制,这对后续学习性能优化至关重要;第三,重点关注页缓存、脏页回写和swap之间的关系,因为线上大部分“内存诡异问题”都出在这一块。学到这里,你已经能超过大多数只会在网上搜“Linux内存过高怎么办”的人了。

回到开头说的那些现象,你回头看其实都不复杂:缓存占用高是Linux的常规操作,进程RES比你想的大是因为虚拟内存和物理内存不是一回事,系统莫名其妙用swap可能是回收策略的正常选择。只要把这些基础机制串成一条线,遇到新问题时就能顺着“地址空间→页表→分配器→回收策略→观测工具”这条链路去定位,而不是对着free的输出干着急。这正是我写这篇文章想传递的东西:内存管理不是一门需要死记硬背的玄学,而是一套可以理解、可以观测、可以验证的系统工程基础。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/30 3:12:15

IEEE 802.3cn-2019 详解:25G/40G EPON 升级的工程实践与避坑指南

简介&#xff1a;IEEE Std 802.3cn-2019是IEEE官方发布的以太网标准修正案&#xff0c;面向网络工程师、数据中心架构师与通信标准研究者&#xff0c;解决高速以太网在单模光纤上长距离传输时缺乏标准化接口的问题。该标准于2019年11月获IEEE SA标准委员会批准&#xff0c;定义…

作者头像 李华
网站建设 2026/9/30 3:12:12

Linux kill命令并不只是杀进程:信号机制、进程状态与故障排查实战

上个月排查一个 Java 服务不响应的问题&#xff0c;登录服务器后我习惯性执行了 kill -9&#xff0c;结果进程还在 ps 输出里挂着&#xff0c;状态栏赫然写着 D。同事调侃说连 kill -9 都杀不死&#xff0c;后来查下来是底层存储故障导致进程进入了不可中断睡眠。这件事让我意识…

作者头像 李华
网站建设 2026/9/30 3:11:37

SpringBoot+Vue茶叶商城实战:前后端分离架构与数据库设计全解析

拿到这套“茶叶商城”项目源码的时候&#xff0c;我第一反应是&#xff1a;又是一套标准的SpringBoot Vue前后端分离商城。但真正翻完代码和数据库脚本之后&#xff0c;发现里面有不少值得细说的东西。这套系统包含了完整的前端页面、后端接口、数据库设计文档&#xff0c;用户…

作者头像 李华
网站建设 2026/9/30 3:10:23

Linux USB CDC设备驱动开发:从描述符解析到内核实现

简介&#xff1a;USB Host驱动CDC设备的完整技术资料&#xff0c;面向嵌入式开发者、驱动工程师和USB协议学习者&#xff0c;重点解决MCU通过USB接口直接识别串口转USB设备并完成数据通信的问题&#xff0c;适用于CH32V307等具备USB Host功能的平台。文档从插入检测、总线复位、…

作者头像 李华
网站建设 2026/9/30 3:09:54

定时任务从crontab到ARQ:状态管理、防重复与可观测性实践

干后端这么多年&#xff0c;定时任务是我又爱又恨的模块。爱的是它确实能扛下大量脏活累活&#xff0c;数据同步、对账、报表生成、缓存预热、心跳检测&#xff0c;全靠它兜底&#xff1b;恨的是它一出问题&#xff0c;排查难度比线上接口挂了还高&#xff0c;因为没有调用方、…

作者头像 李华
网站建设 2026/9/30 3:09:16

闲置U盘别扔!用轻量私有云盒子把旧存储变废为宝

前阵子收拾书房&#xff0c;翻出一抽屉旧数码&#xff1a;三个容量只有4GB、8GB的古董U盘&#xff0c;几张不知道哪年买的TF卡&#xff0c;一个USB 2.0读卡器&#xff0c;还有一块从旧笔记本上拆下来的2.5寸机械硬盘。这东西扔了可惜&#xff0c;留着又不知道能干嘛&#xff0c…

作者头像 李华