我们说了,内存泄露的危害,以及可能在内存不足的时候,系统会做的事情,无非就是OOM杀死进程或者内存回收
对于OOM,其实很简单,就是系统杀死占用大量内存的进程,释放其给更需要的进程
对于内存回收,则是释放可以回收的内存,比如我们说的缓存和缓冲区 Buffer和Cache,就是可回收资源,又被称为文件页
大部分的文件页,都可以直接回收,少部分被修改过的文件页,则需要先写入磁盘,在进行回收
写入磁盘,对于应用程序来说,可以调用fsync,进行脏页的同步
或者交给系统,由内核线程 pdflush负责这些脏页的刷新
除了这种文件页的刷新获取,还有其他的内存可以回收,比如在开启Swap之后,应用程序分配的堆内存,俗称的匿名页,也是可以回收的
在Swap机制中,会将不经常访问的内存先写入到磁盘中,然后释放其,给更加需要的进程使用,再次访问的时候,直接从磁盘读入就可以了
那么我们就针对Swap机制,进行一下了解
Swap很简单,就是讲磁盘当做内存来使用,这个过程中分为了换出和换入两个部分
换出就是将暂时不用的内存存储到磁盘,并释放这些内存
换入就是讲内存再次读到内存中去
Swap就是讲系统的可用内存变大了,即使服务器的内存不足,也可以运行大内存应用程序
现在的快速开机,就是基于Swap,将系统内存存入磁盘,开机的时候,直接加载就可以了
那么Linux触发Swap的机制是什么时候呢?
或者说如何衡量内存是否紧张呢?
常见的场景就是,大内存的分配请求,但是剩余内存不足,这就需要触发Swap,这个过程被称为直接内存回收
除了主动触发,还会有专门的内存线程还定期的回收内存,就是kswapd0,为了衡量内存的使用情况,kswapd0定义了三个内存阈值,分别是,页最小阈值 min,页低阈值 low,页高阈值 high
他们三者和触发Swap的关系如下
如果free内存低于min,则说明内存耗尽
如果大于min小于low,说明需要swap立即回收
如果在low和high之间,就说明还不错,满足内存使用,如果剩余内存大于high,说明毫无压力
如何设置三者,可以通过/proc/sys/vm/中的min_free_kbytes中设置
然后其他两个阈值,就是通过页最小阈值计算生成的
pages_low = pages_min*5/4
pages_high = pages_min*3/2
那么我们查看一下对应的内存和Swap的关系
我们在处理器的NUMA架构中,每个处理器都有自己的内存空间
每一个Node的本次空间,进一步可以分为不同的内存域,比如DMA,普通内存区,伪内存区等
那么我们如何查看对应的内存不同阈值呢?
其实在proc文件系统的中的/proc/zoneinfo
$ cat /proc/zoneinfo
… Node 0, zone Normal pages free 227894 min 14896 low 18620 high 22344 … nr_free_pages 227894 nr_zone_inactive_anon 11082 nr_zone_active_anon 14024 nr_zone_inactive_file 539024 nr_zone_active_file 923986 … |
上面在Node0上,标明了min low high三个阈值,以及free页
剩下的还有
nr_zone_active_anon 和 nr_zone_inactive_anon,分别是活跃和非活跃的匿名页数。nr_zone_active_file 和 nr_zone_inactive_file,分别是活跃和非活跃的文件页数。
上面看出来,free大于页高阈值,所以不会触发swap
当然,某个Node不足的时候,系统可以从其他的Node寻找空闲内存,或者从本次内存中回收内存
这个可以利用同一目录下的zone_reclaim_mode 设置
0是默认,即都可以
1 2 4表示回收本地内存 2表示回写脏数据回收内存, 4表示可以用Swap方式回收内存
这样,就是基本的Swap的使用
最后,在选择匿名页的回收还是文件页的回收上,Linux也提供了对应的选项,配置Swap的回收倾向
在上面的/proc/sys/vm中,我们可以读取swappiness选项,调整使用Swap的积极程度
0-100,数值越大,越倾向于使用Swap,数值越小,越不愿意使用Swap
但是即使设置为0,当小于页高的时候,还是会触发Swap
总结一下
Linux利用文件页回收和匿名文件Swap化来分配内存
对于这个Swap,可以设置min_free_kbytes来回收内存阈值
还可以设置swappiness来调整文件页和匿名页的回收倾向
在Numa架构下,每个Node都有自己的内存空间,本地内存不够的时候,回收策略可以利用zone_reclaim_mode来调整
对JVM来说为何要关闭Swap,则是和JVM的gc相关,在gc的时候会遍历所有用到的堆内存,如果Swap,就会有磁盘IO