《Linux 系统之 OOM 解析.docx》由会员分享,可在线阅读,更多相关《Linux 系统之 OOM 解析.docx(7页珍藏版)》请在课桌文档上搜索。
1、在实际的业务场景中,有没有发现这样一种场景:基于VM环境上面所部署的SpringBoot应用服务,往往在运行过程中将内存利用的足够“狠琐”,常常达到90%甚至以上,此时,很大一部分伙伴就开始叫”了。日:领导,内存不终了,赶紧扩容!(此刻,有大佬肯定在想:扩你妹,整天搞这些没用的)那个傻子是不是瘦了?不知道作为所谓的“技术”人员,大家是如何面对的,如何解决?本文将聚焦于1.inUX内存结构、内存分析以及OOMkiller等3个方面以及笔者多年的实践经验总结来进行解析.内存结构从宏观角度而言,内存管理系统是操作系统最至要的部分之一.在内存管理的系统调用方式,事实上,基于POSIX并没有给内存管理指
2、定任何的系统调用.然而,1.inux却有自己的内存系统调用,主要系统调用如下:系统鞫用描述S=brk(addr)改变数据段大小a=mmap(addr,len,prot,flags,fd,offset)进行映射s=unmap(addr,len)取消映射1.brk通过给出超过数据段之外的第一个字节地址来指定数据段的大小.如果新的值要比原来的大,那么数据区会变得越来越大,反之会越来越小.2、mmap和unmap系统调用会控制映射文件。mmp的第一个参数addr决定了文件映射的地址.它必须是页面大小的倍数.如果参数是0,系统会分配地址并返回a,第二个参数是长度,它告诉了需要映射多少字节。它也是页面大小
3、的倍数.prot决定了映射文件的保沪位,保护位可以标记为可读、可写、可执行或者这些的结合。第四个参数flags能够控制文件是私有的还是可读的以及addr是必须的还是只是进行提示,第五个参数fd是要映射的文件描述符.只有打开的文件是可以祓映射的,因此如果想要进行文件映射,必须打开文件;最后一个参数offset会指示文件从什么时候开始,并不一定每次都要从零开始.针对1.inux内存管理及实现,其实其涉及的面较广,较为宜杂,从计算机早期开始,我们在实际的业务场景中所使用的内存往往都要比系统中实际存在的内存多.为此,内存分配策略克服了这一限制,并且其中最有名的就是引入:虚拟内存(VirtualMemo
4、ry),通过在多个竞争的进程之间共享虚拟内存,虚拟内存得以让系统有更多的内存,以方便维护系统资源的分配.先来张总概览图,具体如下所示:1.inux内存,通常技认为指的是“物理内存,然而,只有内核才可以直接访问物理内存,进程需要访问内存,1.inux内核则需要为每个进程都提供一个独立的虚拟地址空间,访问的是虚拟内存.通常而言,虚拟内存空间的内部被划分为内核空间和用户空间:1、进程在用户态,只能访问用户空间内存2、进程进入内核态才能访问内核空间内存3、每个进程都包含内核空间,但这些内核空间都关联相同的物理内存而针对内存映射,其主要将虚拟内存地址映射到物理内存地址,为了完成内存映射.内核每个进程都维
5、护了一张页表,记录虚拟地址和物理地址的映射关系,页表实际存储在CPU的内存管理单元MMU,这样处理器就可以直接通过硬件找出要访问的内存.再来一张内核线形地址空间布局图,具体可参考如下“硬核”示意图:l-fAcMteSPteX(2Fmg1MtnKryMap(Depend、m*4Uk舟yadRAMiVMA1.1.OCJUStKVtlMUmJmVMAUjOC.OFFSETTASK_S!ZI:KczlPAGt-OFFSCTUnKlpGapIyGES者VnUllOCAddfCMSfwcRXIH)lSIZliFlMdVvllMJA*Mre*VMA1.1.0C-STARTVMA1OC_ENDPKMAPBAS
6、tHXADD1.STARTHXADDR.TOP1.anuX内贼曲泥地址空间无局图针对上述结构图,简单描述如下:1、内核直接映射空间PAGEJDFFSETVMA1.1.OC_START,kmalloc和_get_free_Pageo分配的是这里的页面.二者是借助Slab分配器,直接分配物理页再转换为逻辑地址(物理地址连续)适合分配小段内存。此区域包含了内核镜像、物理页框表mem_map等资源.2、内核动态映射空间VMA1.1.OC_START-VMA1.1.OC_END,gvmalloc用到,可表示的空间大.3、内核永久映射空间PKMAP,BASEFIXADDR,START,kmap4内核临时映
7、射空间FIXDDRSTRTxFIXADDRTOP,kmapatomic内存分析针对内存分析部分,其实可利用的手段或策略较多,基于不同段位的水平高低之分,通常,我们可以借助Top、Free命令以及Vmstat命令进行追踪及观测内存的动态活动变化趋势,以实时了解当前操作系统的资源水位,具体如下所示:(administratorJava1.angOutOfMemoryXtopPIDUSERPRNIVIRTSHRSXCPUXMEMMECOMMAND1root21283279965556S8,080.40:01.03java2root200S.0:.kthreadd基于上述输出结果,简要解析如下:IsV
8、IRI:虚拟内存,包括了进程的代码段、数据段、共享内存、已经申请的堆内存和已经换出的内存等,已经申请的内存,即使还未分配物理内存,也算做虚拟内存2,RSS:常驻内存,是进程实际使用的物理内存,不包括Swap和共享内存3、SHR:共享内存,包括与其他进程共同使用的真实共享内存,包括加载的动态链接库以及程序的代码段4、%MEM:进程使用物理内存占系统内存的百分比(administratorgJavalagOutOfMemory%freetotalusedfreesharedbuff/cacheavailableMem:231744981761826192878417376180O144Swap:2
9、0971482097148此命令行输出内容较为简单:主要打印已用、乘!1余、可用、共享内存以及缓存等信息.部分参数释义如下所示:1、Shared:共享内存,共享内存是通过Tmpfs实现的,它的大小就是Tmpfs使用的内存大小.2,Available:可用内存,是新进程可以使用的最大内存,包括剩余内存和还未使用的内存.3、BufferZCache:缓存包括两部分,一部分是磁盘读取文件的页缓存,用来缓存从磁盘读取的数据,加速以后再次访问速度,另一部分是Slab分配的可回收镒存;缓冲区是对原始磁盘的临时存储,用来缓存将要写入磴盘的数据,统一优化磴盘写入.(administratorJava1.ang
10、OutOfMemoryXvmstat11procsmemory-swap-io-system-cpurbswpdfreebuffcachesisobiboincsussyidwast11815348218111872001011110010000基于上述埔出结果,简要解析如下:1、si:换入,每秒从磁盘读入虚拟内存的大小,若此值长时间持续大于0,表示物理内存不够或者内存泄漏,需要定位问题.2,so:换出,每秒从内存写入磁盘的大小,若此值长时间持续大于0.表示物理内存不够用,需要排查内存问趣.OOMKiller通常有这样的一种场品:若一台VM(虚世机)上部署多个应用服务,此处,哲以SpringB
11、oot微服务为例,在某种特殊的时刻,例如:业务促销、压力测试或当某一个联机负载节点或因网络抖动而挂掉时,此台VM上的服务突然在毫无征兆的情况下,突然被挂掉。与此同时,我们开始搜集相关线索,以便能够快速定位到问题原因,将罪魁祸三抓捕归案.那么,为什么会出现这种问题?它是如何产生的?OoM,全称为“OutOfMemory,即内存溢出.00MKiller是1.inux自我保护的方式,防止内存不足时出现严函问题.1.inux内核所采用的此种机制会时不时监控所运行中占用内存过大的进程,尤其针对在某一种瞬间场景下占用内存较快的进程,为了防止操作系统内存耗尽而不得不自动将此进程Kill掉.通常,系统内核检测
12、到系统内存不足时,筛选并终止某个进程的过程可以参考内核源代码:linux/mm/oom_kill.c,当系统内存不足的时候,OUJof_memory()被触发.然后调用SeleCJbaC1.ProCeSSo选择一个bad进程杀掉.如何判断和选择一个bad进程呢?IJnUX操作系统选择bad进程是通过调用oom_badness(),挑选的算法和想法都很简单很朴实:Sbad的那个进程就是那个最占用内存的进程.00MKiller源码解析OOMkiller的核心函数是OUjo1.memory(),执行流程如下:1、调用CheCk-PaniJon_Oomo检查是否允许执行内核恐慌,假如允许,需要点启系统
13、.2、若定义了/proc/sys/vm/oom_kill_allocating_task即允许Kill掉当前正在申请分配物理内存的进程,那么杀死当前进程。3、调用SeIeCt_bad_PrOCeSS,选择badnessscore最高的进程.4、调用oom_km_process,杀死选择的进程。我们通过分析BadnessScore的计算函数来理解OoMKiller是如何选择需要被Kill掉的进程,具体源代码可参考如下所示:unsignedlongoom-badness(structtask_struct*p,structmem-cgroup*memcg,constodemask-t*odenas
14、k,unsignedlongtotalpages)longpoints;longadj;*假如该进程不能被kill,则分数返回0./if(oom_unkillable_task(p,memcg,nodemask)return;pfind_lock_task_nw(p);if(Ip)return;*获取该进程的oom_SCore_adj,这个是用户为进程设置的badnessscore*调整值,假如这个值为-I。或者迸程被标记为不可被kill,或者进程处于Vfork()过程,badnessSCOre返回0./adj-(long)p-signal-oom_score_adj;if(adj三三OOM_
15、SCORE_ADJ_MINtest_bit(MMF_OOM_SKIP,Sp-m-flags)in-vfork(p)task-u11lock(p);returni)*badnessSCOre分数-物理内存以数+交换区页数+页表PageTableftA.*/points=get_mm_rss(p-inn)+get_mm_counter(p-mm,MMSWAPENTS)+mm_pgtables_bytes(p-mm)/PAGE_SIZE;task-unlock(p);*利用以下公式对badnessscore值进行调整./adj-totalpages/100;pointsadj;*返回badnessscore,假如等于。,则返回1.*/returnpoints?points:1;通过对BadnessScore计兑函数的分析,我们可以发现OOMKiller是基于RSS即常驻的物理内存来选择进程进行Kill操作,从而释放相关内存以进行系统自我保护.有关OOMKiller相关配舌、直看及分析将于后续文盲给出,大家到时留意直看.综上所述,本篇文章主要通过基于对1.inux内存结构.分析及OOMKilIer3个核心维度,从主动及被动场景等2方面对1.inux操作系统内存的剖析,以探讨在实际的业务场品中,内存表现的相关活动及经脸认知.大家有什么疑问、想法及建议,欢迎雷吉沟通。