[Linux性能优化]二:内存

基础篇

1 基本概念

  Linux内核给每个进程都提供了一个独立的虚拟空间,并且这个地址空间是连续的。这样,进程就可以很方便地访问内存,更确切地说是访问虚拟内存。

虚拟地址空间内部又被分成内核空间用户空间。32位和64位系统的内存寻址空间,如下图:

  进程在用户态时,只能访问用户空间内存;只有进入内核态后,才能访问内核空间内存。这样,进程切换到内核态后,就可以方便的访问内核空间内存。既然每个进程都有这么大的地址空间,

那么所有进程的虚拟内存加起来,自然要比实际的物理内存大的多。所以,并不是所有的虚拟内存都会分配物理内存,只有那些实际使用的虚拟内存才分配物理内存,而且分配后的物理内存,是通过内存映射来管理的。

  内存映射:就是将虚拟内存地址映射到物理内存地址。为了完成内存映射,内核为每个进程都维护了一张页表,记录虚拟地址与物理地址的映射关系。

  MMU并不以字节为单位来管理内存,而是规定一个内存映射的最小单位,也就页,通常是4kB,这样每一次内存映射,都需要关联4kB或者4kB整数倍的内存空间。这样仅32位系统就需要100多w页表项(4GB/4kB),才能实现整个地址空间的映射。所以出现了多级页表大页(HugePage)来解决这个问题

 

  虚拟内存的空间分布

  了解虚拟内存的分布情况,我们以下图为例

  • 只读段: 代码和常量
  • 数据段:全局变量
  • 堆:动态分配内存,从低地址开始向上增长
  • 文件映射段: 动态库、共享内存
  • 栈: 局部变量,函数调用的上下文,栈大小一般固定是8MB

 

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

 

2 分析内存的使用

  内存回收

  • 回收缓存,比如使用LRU算法,回收最近使用最少的内存页面
  • 回收不常用的内存,把不常用的内存通过交换分区直接写到磁盘(swap)
  • 杀死进程,内存紧张时系统会通过(OOM)直接干掉进程

  

  关于OOM,linux系统通过oom_score为每个进程的内存使用情况进行评分:

  • 一个进程消耗的内存越大,oom_score就越大
  • 一个进程运行占用的CPU越多,oom_score就越小 

 这样可以更好的保护系统。当然为了实际需要,管理员可以通过设置某个进程的 proc文件系统的 oom_adj来调整oom_score。 oom_adj的范围是[-17, 15],数值越大表示进程越容易被oom杀死

echo -16 > /proc/$(pidof sshd)/oom_adj

  生产上java服务,我发现oom_score都达到了200+,有的甚至600+

salt '*' cmd.run 'ps -ef|grep java|grep target|grep -v grep|awk "{print \"cat /proc/\"\$2\"/oom_score\"}"'​

  

 

  查看内存的使用情况: 

$ free -m
              total        used        free      shared  buff/cache   available
Mem:          15713        3351        7945         594        4417       11487
Swap:          4095           0        4095

  物理内存Mem和交换分区Swap分成两个表。分别表示为:

  • total: 总内存大小;
  • used: 已用内存大小,包含共享内存
  • free: 未用内存的大小
  • shared: 共享内存的大小
  • buff/cache: 缓存和缓冲区的大小
  • available: 新进程可用内存的大小,不仅包含未使用内存,还包含了可回收的缓存,所以一般比未使用内存更大。不过不是所有缓存都可以回收,因为有些缓存可能正在使用中。

  

  其中缓存是Buffer和Cache两部分的总和。Buffer是缓冲区,Cache是缓存,两者都是数据在内存中的临时存储。

  • Buffers
    • 是内核缓存区用到的内存,对应的是/proc/meminfo中的Buffers值
    • 深层次上来说buffers是原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别的大(20MB左右),这样,内核就可以把分散的写集中起来,统一优化磁盘吸入

  • Cache
    • 是内核页缓存和 Slab 用到的内存,对应的是/proc/meminfo 中的Cached与SReclaimable之和。
    • 从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据,这样下次访问的时候就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。
    • 关于SReclaimable是Slab的一部分,Slab包括可回收和不可回收部分

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

 

   我们知道free是整个系统的内存使用情况,如果看单个进程,则使用top或者ps

# top
..........
Mem:  32864280k total,  4563552k used, 28300728k free,   377268k buffers
Swap: 20479996k total,        0k used, 20479996k free,  1906592k cached

   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                              
  3298 root      20   0  698m 572m 1272 S  2.5  1.8 903:53.47 redis-server 10.3.90.5:7001 [cluster]
  3294 root      20   0 1239m 1.1g 1260 S  1.2  3.5 435:42.90 redis-server 10.3.90.5:7000 [cluster]
     1 root      20   0 19368 1552 1232 S  0.0  0.0   0:31.10 /sbin/init    
  • VIRT是进程虚拟内存的大小,只要是进程申请过的内存,即便是还没有真正分配物理内存,也会计算在内
  • RES是常驻内存的大小,也就是进程实际使用的物理内存大小,但不包括SWAP和共享内存
  • SHR是共享内存的大小,比如与其进程共同使用的共享内存、加载的动态链接库及程序的代码段等
  • %MEM是进程使用物理内存占系统总内存的百分比

 需要注意的两点信息:

  1 虚拟内存通常并不会全部分配物理内存。从上面的输出看到每个进程的虚拟内存都比常驻内存大一些

  2 共享内存SHR并不一定是共享的,比如说程序的代码段、非共享的动态链接库,也都算在SHR里面。当然SHR也包括了进程间真正共享的内存,所以在计算的时候不能把所有进程的SHR直接相加。

  

 

案例篇

1 利用缓存优化程序的运行效率

  我们知道Buffer和Cache是为了提升系统的I/O性能,利用内存,充当起慢磁盘和快CPU直接的桥梁,可以加速I/O的访问速度。

  缓存命中率

  可以使用cachestat和cachetop这两个工具,来观察系统的和进程的缓存命中率情况。

  • cachestat 提供了整个系统缓存的读写命中率
  • cachetop 提供了每个进程的缓存命中情况

  不过要注意,cache和buffers都是操作系统来管理的,应用程序并不能直接控制这些缓存的内容和生命周期。程序内部可以使用栈或者栈明确声明内存空间,来存储需要缓存的数据。再或者,使用Redis这类外部缓存服务。

 

2 内存泄露,应该如何定位和处理

  内存的分配和回收

  

3 为什么系统的swap变高

#free -m
              total        used        free      shared  buff/cache   available
Mem:           7821        4131        1210         412        2479        2953
Swap:          3967        1266        2701

  

关于内存管理中,通常被叫做文件页。大部分的文件页,都可以直接回收,以后有需要时,再从磁盘重新读取,而哪些被应用程序修改过 ,并且暂时还没有写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能进行内存释放。这些脏页,一般可以通过两种方式写入磁盘。

  • 可以在应用程序中,通过系统调用fsync,把脏页同步到磁盘中
  • 也可以交给系统,由内核线程pdflush负责这些脏页的刷新

如果一些内存在分配后很少被访问,似乎也是一种资源浪费。而Linux的Swap机制就是减少这样的浪费。Swap把这些不常访问的内存先写入到磁盘中,然后释放这些内存,给其他更需要的进程用。

 

SWAP工作原理

说白了swap就是把一块磁盘空间或者一个本地文件,当成内存来使用。它包括两个过程:

  • 所谓换出,就是把进程暂时不用的内存数据存到磁盘中,并释放这些数据占用的内存
  • 换入,则是进程再次访问这些内存的时候,把它们从磁盘读到内存中来

 

用到swap时候的情况:

  • 直接内存回收: 有大块的内存分配请求,但是剩余内存已经不足了,这时候系统就需要回收一部分内存(比如缓存),进而满足新内存请求
  • kswapd0:专门定期回收内存的内核线程

为了衡量内存的使用,我们用页最小阈值(pages_min)、页最低阈值(pages_low)和页最高阈值(pages_high)。剩余内存,则使用pages_free表示:

 

kswapd0定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。

  • 剩余内存小于页最小阈值,说明进程可用内存都消耗尽了,只有内核才能分配内存。
  • 剩余内存落在页最小阈值

 

 

套路篇

 

操作篇

释放cache

* echo 1 > /proc/sys/vm/drop_caches

To free dentries and inodes:
* echo 2 > /proc/sys/vm/drop_caches

To free pagecache, dentries and inodes:
* echo 3 > /proc/sys/vm/drop_caches  # 注意这会将buffer cache

 

swap

swapoff -a && swapon -a
mkswap /dev/hdb2
swapon /dev/hdb2
最后修改 fstab

4GB of RAM or less a minimum of 2GB of swap space
4GB to 16GB of RAM a minimum of 4GB of swap space
16GB to 64GB of RAM a minimum of 8GB of swap space
64GB to 256GB of RAM a minimum of 16GB of swap space
256GB to 512GB of RAM a minimum of 32GB of swap space

 


在sysctl 中设置 vm.swappin,用来限制swap的使用, 即使是设置为0, 但是必要的时候还是会使用到swap,因为这个参数是衡量是否使用swap的取向


利用 mclog 查询内存和cpu问题

使用ipcs -m 查看共享内存的情况

# ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x6c001300 0          zabbix     600        365056     6                       
0x6d001300 32769      zabbix     600        50648      2  

  

posted @ 2019-07-23 18:40  richardzgt  阅读(649)  评论(0编辑  收藏  举报