Linux系统性能优化二:详解Linux内存原理

文章目录

        • Linux内存是怎么工作的
          • 通过虚拟地址访问物理地址
          • 内存的分配与回收
          • Linux的swap机制
          • 查看内存
        • 如何理解cache和buffer
        • 如何快准狠定位内存问题

Linux内存是怎么工作的

通过虚拟地址访问物理地址

首先我们8g的内存,其实指的是物理内存,也就是主存,

在Linux系统里边,我们的进程是不能直接访问物理内存的,它会为每个进程分配一个虚拟地址内存,然后通过虚拟地址内存去访问,

在CPU的内存管理单元里边有一个页表,它会把我们的虚拟内存跟物理内存之间做一个映射,

只有那些实际使用的虚拟内存才会分配物理内存,也就是提供一个内存映射的关系

当进程访问的虚拟地址在列表中查不到的时候,就会产生一个缺页异常,然后就会重新分配物理内存,然后更新页表,再返回给用户空间,这样进程就恢复正常

内存映射的最小单位是页,这个页一般是比较小的,一般是4kb左右,

它页比较小,就有一个问题,因为这个页比较小,所以需要比较多的页才能提供这个内存映射的关系,所以整个页表会变得非常大,

那么里面为了解决这个页表变得非常大的问题,Linux有两种机制,一个是多级页表,一个是大页,

Linux的多级页表采用的是4级页表,它通过将原来的映射关系,改成区块索引以及区块内的偏移,

因为虚拟内存空间通常只用到了很少的一部分,多级页表就只保存这些用到的虚拟内存,这样就可以减少那个页表的数量,

还有一种机制是大页,也就是分配比一般的页更大的页,常见的有2MB,1GB,大页通常用在那些使用大量内存的进程上,比如 Oracle,还有es,logstash他们占用的内存也挺多的,

内存的分配与回收

malloc() 是 C 标准库提供的内存分配函数,对应到系统调用上,有两种实现方式,即 brk() 和 mmap()。

对小块内存(小于 128K),C 标准库使用 brk() 来分配,也就是通过移动堆顶的位置来分配内存。这些内存释放后并不会立刻归还系统,而是被缓存起来,这样就可以重复使用。

而大块内存(大于 128K),则直接使用内存映射 mmap() 来分配,也就是在文件映射段找一块空闲内存分配出去。

brk() 方式的缓存,释放后不会立即归还给系统,可以减少缺页异常的发生,提高内存访问效率。当时,因为这些内存没有归还系统,所以在内存工作繁忙时,频繁的内存分配和释放会造成内存碎片。

而 mmap() 方式分配的内存,会在释放时直接归还系统,所以每次 mmap 都会发生缺页异常。在内存工作繁忙时,频繁的内存分配会导致大量的缺页异常,使内核的管理负担增大。这也是malloc 只对大块内存使用 mmap 的原因

对内存来说,如果只分配而不释放,就会造成内存泄漏,甚至会耗尽系统内存。所以,在应用程序用完内存后,还需要调用 free() 或 unmap() ,来释放这些不用的内存

在发现内存紧张时,系统也有一系列机制来回收内存,

  • 回收缓存,使用LRU(least Recently Used)算法,对那些最近最少使用的的内存页面进行回收
  • 将那些不常访问的内存放到swap交换分区
  • OOM机制(out of memory),直接杀掉占用大量内存的进程。
Linux的swap机制

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

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

  • 换出就是把进程暂时没有用到的不常用的内存放到磁盘里面
  • 换入就是当进程想再次访问这些内存的时候,再把它从磁盘换到内存

swap它把我们系统的可用内存变大了,但是因为硬盘比内存慢得多,所以如果swap被大量使用了,就要进一步排查。

平时我们笔记本电脑休眠的时候,利用的就是swap的原理,休眠时,把系统的内存存入磁盘,这样等到再次开机时,只要从磁盘中加载内存就可以。这样就省去了很多应用程序的初始化过程,加快了开机速度。

swap是用来回收内存的,对于回收内存,最容易想到的场景就是有一个新的大块内存需要分配,但是现在内存不够,这时候就需要回收一部分内存,它被称为直接内存回收

另外,还有一个专门的内核线程用来定期回收内存,叫kswapd0。kswapd0 定义了三个内存阈值(watermark,也称为水位),分别是

页最小阈值(pages_min)、页低阈值(pages_low)和页高阈值(pages_high)。剩余内存,则使用 pages_free 表示。
Linux系统性能优化二:详解Linux内存原理_第1张图片

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

另外,也有可能出现可用内存还有很多,但是swap被使用得也很多。可能是因为一些进程的内存占用比较大,所以占用了swap内存,但是当这个进程结束的时候,它的可用内存就恢复得比较大了。但是由于之前他占用了交换分区,交换分区里面的内存还没有放到可用内存里边,还是放在磁盘里边的,所以就会造成这种情况发生。

查看内存

free

[root@VM_0_11_centos ~]# free
              total        used        free      shared  buff/cache   available
Mem:        1882056      385240       78224         624     1418592     1308716
Swap:             0           0           0

最后一列的可用内存 available 。available 不仅包含未使用内存,还包括了可回收的缓存。但是,并不是所有缓存都可以回收,因为有些缓存可能正在使用中。

free 显示的是整个系统的内存使用情况。如果你想查看进程的内存使用情况,使用 top

[root@VM_0_11_centos ~]# top
top - 20:22:42 up 25 days,  5:36,  1 user,  load average: 0.00, 0.01, 0.05
Tasks:  90 total,   2 running,  88 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1882056 total,    74196 free,   385740 used,  1422120 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1308224 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                
    1 root      20   0  191040   2852   1408 S  0.0  0.2   4:43.55 systemd                                                                                                
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.02 kthreadd                                                                                               
    4 root       0 -20       0      0      0 S  0.0  0.0   0:00.00 kworker/0:0H                                                                                           
    6 root      20   0       0      0      0 S  0.0  0.0   0:32.51 ksoftirqd/0    
  • VIRT 是进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正分配物理内存,也会计算在内。
  • RES 是常驻内存的大小,也就是进程实际使用的物理内存大小,但不包括 Swap 和共享内存。
  • SHR 是共享内存的大小,比如与其他进程共同使用的共享内存、加载的动态链接库以及程序的代码段等。
  • %MEM 是进程使用物理内存占系统总内存的百分比。

如何理解cache和buffer

在free命令里面其他指标都很好理解,然后有个buffer和cache,它的字面意识是buffer是缓冲,cache是缓存,之前我man free去查看过他的定义,

buffers
              Memory used by kernel buffers (Buffers in /proc/meminfo)
 
       cache  Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
 
       buff/cache
              Sum of buffers and cache

他的帮助手册里面显示:

  • buffer是用于内核缓冲区的内存大小
  • cache是用于页缓存和slabs的内存大小

它的帮助手册里面告诉我们,这些数值都来自 /proc/meminfo。实际上,很多的性能分析工具,他们的数据都是从/proc/这个目录里面获取的。所以我们可以继续man一下proc目录,

它这里的定义是

  • buffer是对原始磁盘数据块的临时存储,也就是缓存磁盘的数据,通常只有20兆左右,它是用来优化数据写入磁盘的,可以把多次分散的写集中起来,统一成一次来写入,。
  • Cached 是从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。这样,下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。

Buffer 的文档没有提到这是磁盘读数据还是写数据的缓存,而在很多网上资料里面,都会提到 Buffer 只是对将要写入磁盘数据的缓存

文档中提到,Cache 是对从文件读取数据的缓存,那么它是不是也会缓存写文件的数据呢?

我之前使用dd命令分别进行磁盘和文件的写和读的实验,然后开另一个终端用vmstat来观察,发现:

  • Buffer 既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”。
  • Cache 既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”。

简单来说,Buffer 是对磁盘数据的缓存,而 Cache 是文件数据的缓存,它们既会用在读请求中,也会用在写请求中。

如何快准狠定位内存问题

你可能感兴趣的:(Linux系统性能优化)