问题背景:
在嵌入式Linux系统产品稳定性测试过程中,我们最关心也是最重要的问题是内存泄漏问题。
一般嵌入式产品内存容量比较有限,我们除了分析代码层次的内存泄漏缺陷外,还实时监测系统运行的内存状态。以免过度使用内存导致系统将进程kill掉的情况。本文主要分析下Cache内存占用的原因,解决办法以及对系统运行的影响;
问题分析:
在嵌入式Linux系统中使用free命令可以看到如下信息:
# free
total used free shared buffers cached
Mem: 247400 123764 123636 33236 7728 63292
-/+ buffers/cache: 52744 194656
Swap: 0 0 0
#
可以简单看到内存的总容量、free容量、缓存占用的buffers、cached容量;
Linux下内存占用多的原因和机制为:
当linux第一次读取一个文件运行时,一份放到一片内存中cache起来,另一份放入运行程序的内存中,正常运行,当程序运行完,关闭了,cache中的那一分却没有释放,第二次运行的时候,系统先看看在内存中是否有一地次运行时存起来的cache中的副本,如果有的话,直接从内存中读取,那样,速度就快多了。
说明这种情况的很典型的例子是启动firefox,由于firefox程序很大,因此第一次读取运行的时候很慢,尤其在速度不快的机器上,但是当你彻底关闭了firefox,ps看不到一个firefox进程,第二次再启动的时候就比第一次明显快很多,这是由于这次系统是直接从cache中读取的firefox来运行,并不是从磁盘上读取的。
再有一个例子:我们频繁使用的ls命令等基本命令,你运行的时候根本看不到硬盘灯闪,因为这些常用的命令都是再第一次运行后就保存在cache中的,以后就一直从内存中读出来运行。
如果cache占用的内存过多了,影响正常运行程序需要的内存,那么会释放掉一部分cache内存,但是总量会保持一个很高的值,所以,linux总是能最大限度的使用内存,就算加到16G,32G内存,也会随着不断的IO操作,内存的free值会慢慢减少到只有几M,想要内存不发生这种情况,只有一个办法:把内存加到比硬盘大。
解决办法:
手动释放cache的办法:
# free
total used free shared buffers cached
Mem: 247400 123756 123644 33240 7728 63296
-/+ buffers/cache: 52732 194668
Swap: 0 0 0
# echo 3 > /proc/sys/vm/drop_caches
# free
total used free shared buffers cached
Mem: 247400 99744 147656 33240 288 47132
-/+ buffers/cache: 52324 195076
Swap: 0 0 0
#
使用echo 3 > /proc/sys/vm/drop_caches可以看到buffers和cache占用的内存明显减小;
当然,这个文件可以设置的值分别为1、2、3。它们所表示的含义为:
echo 1 > /proc/sys/vm/drop_caches:表示清除pagecache。 echo 2 > /proc/sys/vm/drop_caches:表示清除回收slab分配器中的对象(包括目录项缓存和inode缓存)。slab分配器是内核中管理内存的一种机制,其中很多缓存数据实现都是用的pagecache。 echo 3 > /proc/sys/vm/drop_caches:表示清除pagecache和slab分配器中的缓存对象。
Linux内核会在内存将要耗尽的时候,触发内存回收的工作,以便释放出内存给急需内存的进程使用。一般情况下,这个操作中主要的内存释放都来自于对buffer/cache的释放。尤其是被使用更多的cache空间。既然它主要用来做缓存,只是在内存够用的时候加快进程对文件的读写速度,那么在内存压力较大的情况下,当然有必要清空释放cache,作为free空间分给相关进程使用。所以一般情况下,我们认为buffer/cache空间可以被释放,这个理解是正确的。
但是这种清缓存的工作也并不是没有成本。理解cache是干什么的就可以明白清缓存必须保证cache中的数据跟对应文件中的数据一致,才能对cache进行释放。所以伴随着cache清除的行为的,一般都是系统IO飙高。因为内核要对比cache中的数据和对应硬盘文件上的数据是否一致,如果不一致需要写回,之后才能回收。
# cat /proc/meminfo
MemTotal: 247400 kB
MemFree: 147436 kB
MemAvailable: 158788 kB
Buffers: 440 kB
Cached: 47156 kB
SwapCached: 0 kB
Active: 33752 kB
Inactive: 41008 kB
Active(anon): 31000 kB
Inactive(anon): 29408 kB
Active(file): 2752 kB
Inactive(file): 11600 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 4 kB
Writeback: 0 kB
AnonPages: 27212 kB
Mapped: 22920 kB
Shmem: 33252 kB
Slab: 14684 kB
SReclaimable: 4700 kB
SUnreclaim: 9984 kB
KernelStack: 2880 kB
PageTables: 1232 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 123700 kB
Committed_AS: 502804 kB
VmallocTotal: 258867136 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
使用cat /proc/meminfo命令我们可以看到MemFree和MemAvailable的容量是不一样的,说linux系统可使用内存不够是系统会立即去释放cache的内存,已维持系统进程正常运行,只有当cache内存也不足时,(即MemAvailable非常小的时候)系统才会kill掉部分进程。