Linux OOM 基本原理解析

本文详细介绍了Linux内存管理中的OOM(Out Of Memory)基本原理,包括内存分配管理、进程内存空间布局、内存回收机制,以及OOM Killer的工作方式。强调了内核如何在物理内存不足时进行回收,包括同步和异步回收策略。此外,还提到了Android系统中的Low Memory Killer(LMK)机制,作为系统内存不足时的补充。通过对这些内容的理解,有助于开发者更好地理解和排查内存相关问题。
摘要由CSDN通过智能技术生成

1.序言

内存对计算机系统来说是一项非常重要的资源,直接影响着系统运行的性能。最初的时候,系统是直接运行在物理内存上的,这存在着很多的问题,尤其是安全问题。后来出现了虚拟内存,内核和进程都运行在虚拟内存上,进程与进程之间有了空间隔离,增加了安全性。进程与内核之间有特权级的区别,进程运行在非特权级,内核运行在特权级,进程不能访问内核空间,只能通过系统调用和内核进行交互,内核会对进程进行严格的权限检查和参数检查,使得系统更加安全。通过虚拟内存访问物理内存,每次都需要解析页表,这大大降低了内存访问的性能,为此CPU的MMU里面加入了TLB用来缓存页表解析的结果,这样由于程序的时间局部性和空间局部性,能极大的提高内存访问的速度。虽然和直接访问物理内存相比,仍然存在着一些性能损耗,但是损耗已经降到很低了。因此虚拟内存机制在系统安全和性能之间达到了最大的平衡。

虽然如此,但是虚拟内存机制也使得计算机的内存系统变得异常复杂,给我们的编程带来了巨大的挑战。内存问题,在很多软件公司里面,都是一个非常重要非常让人头疼的问题,今天我们从OOM的角度来帮大家提高一点内存方面的知识,虽然不能说帮助人们来完全解决内存问题,但是也能从一个侧面来提高大家分析内存问题相关的能力。

2.内存的分配管理

我们已经知道了物理内存、虚拟内存、用户空间、内核空间之间的区别,下面我们再来深入的了解一下这方面的知识。系统刚启动的时候是运行在物理内存之上的,然后系统建立了一段足够自己继续运行的恒等映射的页表,也就是把物理地址映射到相同地址的虚拟地址上。等到系统再进一步初始化之后,就会建立完整的页表来映射物理内存,并把内核映射在虚拟地址空间的高部位,对于32位系统来说是3G之上的内存空间,对于64系统来说,是映射到比较接近虚拟地址顶端的地方。内核初始化之后就会启动init进程,从而启动整个用户空间的所有进程。内核空间和用户空间的内存管理方式的差别是非常大的,首先内核是不会缺页也不会换页的,不会缺页是指内核的物理内存在启动时就直接映射好了,使用时直接分配就行了,分配好虚拟内存的同时物理内存也分配好了。不会换页是指,当系统内存不足时内核自身使用的物理内存不会被swap出去。

与此相反,用户空间的内存分配是先分配虚拟内存,此时并不会直接分配物理内存,而是延迟到程序运行时访问到哪里的内存,如果这个内存还没有对应的物理内存,MMU就会报缺页异常从而陷入内核,执行内核的缺页异常handler给分配物理内存,并建立页表映射,然后再回到用户空间刚才的那个指令处继续执行。当系统内存不足时,用户空间使用的物理内存会被swap到磁盘,从而回收物理内存。之后如果进程再访问这段内存又会再发生缺页异常从swap处把内存内容加载回来。

3.进程的内存空间布局

明白了上面这些,我们再来看看进程的用户空间内存布局。我们都知道进程的内存空间是由代码区、数据区、堆区、栈区组成。我们先来看下面的图,我们以32位进程为例进行讲解,64位的数值太大不好画的,但是原理都是一样的。

进程启动之后的内存布局如上图所示,程序file的代码段被映射到text区,数据段映射到data区,内核还会帮进程建立堆内存区映射和栈内存区映射,堆一般紧挨着data区的末尾往上增长,栈区在3G下面一点点往下增长。数据区和代码区是在进程启动时由内核之间分配好的,之后大小就不会再改变,heap区是随着程序运行中不断的malloc/free而增长或者缩小的,stack区是随时程序运行的局部变量分配释放而变化的,局部变量的分配释放是自动的,因此这三个区域也分别被叫做静态内存、动态内存、自动内存。由此我们可以看出,我们不必对静态内存、自动内存太操心,我们最应该关系的是动态内存。我们可以brk系统调用扩大heap区域来增加堆内存,然后再自己管理使用堆内存,但是这样做显然很麻烦。因此C库为我们准备了相关的API,malloc、free,来分配和释放堆内存,这样就方便到了。

C库里面最早的malloc实现叫做dlmalloc,在计算机早期还是单CPU时代的时候非常流行,效率也非常高,但是随着SMP多CPU时代的到来,dlmalloc的缺点也越来越明显,尤其是多线程同时调用malloc的时候,锁冲突越来越严重,严重影响了性能。后来业界相继出现了ptmalloc、jemalloc、scudo等优秀的malloc库。

Ptmalloc是Glibc的默认malloc实现,jemalloc库是首先实现在FreeBSD的malloc库,后广泛应用于FireFox、Redis、Netty等

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值