Linux添加Swap分区详解,解决Tomcat自动关闭问题

发布时间:

最后更新:

1. Tomcat自动关闭的问题 #

刚把本站写完,兴冲冲地放到我那512M内存的低端VPS上,结果跑了没多久就莫名其妙地挂了。拿ps -aux一看Tomcat的进程没了,以为是自己的程序出了BUG,于是便到其logs目录检查日志,却没发现任何错误。

按着重启-重装-重买的三步走,虽然不知道怎么挂了,但再启动一遍总没错,于是没过多久它又挂了......接连启动几次都是以挂掉为终,而且还没有规律,更无任何日志记录。加上-XX:+HeapDumpOnOutOfMemoryError参数之后也没有生成任何转储文件。想起此时我的心中顿时一千头秘鲁羊驼飞奔而过,震惊!Tomcat竟自动关闭!(震惊部已满

好在这个问题并不罕见,经过一番搜索便发现了其原因,原来Linux有个好,就是内存不足时杀进程比Windows还快。Linux 系统里有个叫 OOM Killer的机制,该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核会把该进程杀掉:

The functions, code excerpts and comments discussed below here are from mm/oom_kill.c unless otherwise noted.

It is the job of the linux 'oom killer' to sacrifice one or more processes in order to free up memory for the system when all else fails. It will also kill any process sharing the same mm_struct as the selected process, for obvious reasons. Any particular process leader may be immunized against the oom killer if the value of its /proc/<pid>/oomadj is set to the constant OOM_DISABLE (currently defined as -17).

The function which does the actual scoring of a process in the effort to find the best candidate for elimination is called badness(), which results from the following call chain:

_alloc_pages -> out_of_memory() -> select_bad_process() -> badness()

OOM Killer也不会闷声杀进程,它的操作都有日志记录,使用dmesg | grep -i 'killed process'查看一下,果不其然好几条记录,可怜的Tom猫就这样一次次被残忍的结束。

既然知道了病根就好解决了,上面提到了可以使用oomadj来禁止OOM Killer作用于某个进程上,作为一种临时方法是可以的,但是回头一想人家设计个OOM Killer也不是闲的无聊,内存作为进程运行的必需资源,不够了当然需要像个法子解决,把不重要的进程杀掉保障更重要的进程也是理所当然,使用oomadj文件禁止它治标不治本,想办法腾出内存才是根本的出路。

首先呢,最好的解决方法就是升级硬件,有句话说得好:能花点钱升级硬件就不要瞎折腾,可惜这话是对企业说的,不适用于一个穷学生党。不过作为折腾过Linux的人,自然就想到了Swap分区这个东西。

2. SWAP分区简介 #

Linux内核为了提高读写效率与速度,会将文件在内存中进行缓存,这部分内存就是Cache Memory(缓存内存)。即使你的程序运行结束后,Cache Memory也不会自动释放。这就会导致你在Linux系统中程序频繁读写文件后,你会发现可用物理内存变少。当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap空间中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。这样,系统总是在物理内存不够时,才进行Swap交换。

简单来说,SWAP分区能够将内存中不常用的一部分挪到硬盘上,给急需内存的程序腾位置,硬盘上缓存的数据在需要使用时再挪回去,可以看做把硬盘的一部分空间作为了内存使用。

系统在什么情况或条件下才会使用Swap分区的空间呢? 其实是Linux通过一个参数swappiness来控制的,当然还涉及到复杂的算法,这里就不详细讲解了。

另一个问题是SWAP分区设置多大才好呢?在Centos的官方文档上有一个公式

bash
If M < 2
	S = M * 2
Else
	S = M + 2

M是物理内存大小,S是交换分区大小,公式中的单位都是GB。对于本站使用的512M内存,SWAP就设置为1G.

我记得之前买这家的VPS是自带交换分区的,拿free一看,SWAP一行的右边是干净的3个0,难怪破机子老杀我进程。回去查看了VPS供应商的官网发现了原因,原来之前买的VPS是用的OpenVZ虚拟化技术,由宿主机统一设置SWAP;为了用上TCP-BBR,新买的机子选择了KVM虚拟化+自己更新内核,这就得自个配置交换分区了。在查阅了若干资料后,终于搞懂了配置SWAP分区的方法。

3. 手动设置SWAP分区 #

shell
dd if=/dev/zero of=/var/swap bs=1M count=1024
mkswap -v1 /var/swap
swapon /var/swap
echo "/var/swap swap swap defaults  0 0" >> /etc/fstab

首先上整套命令,仅仅需要4句即可,还是挺简单的,其中第一条命令需要等一会才能执行完毕。全部运行完成之后用free -m查看一下,最下面一行会显示SWAP的大小,不是0就说明配置完成。不过咱不能仅满足于解决问题,还得理解这几条命令的作用。

虚拟内存是把内存中的数据存到硬盘上,硬盘上存储表现为文件,所以第一步需要创建一个用来保存内存数据的文件,使用了命令

shell
dd if=/dev/zero of=/var/swap bs=1M count=1024

dd是一个用来复制文件内容的命令,它提供了比cp更高级的功能,如指定复制的长度、在复制时进行一些转换等等,这里用它来创建SWAP文件。

整体的意思就是从/dev/zero这个虚拟的文件里复制1M * 1024= 1G的0内容到/var/swap文件,其结果就是创建了一个1G大小的空文件,这个文件将作为SWAP使用。

shell
mkswap -v1 /var/swap
swapon /var/swap

这两句十分简单,mkswap的作用类似于格式化,它将制定的文件转换成交换可以作为分区的文件。swapon命令将启用指定的SWAP文件,当然有启用也就有关闭,关闭的 命令是swapoff。这两句执行完后用free看看SWAP的大小,不是0说明成功添加了交换分区。

shell
echo "/var/swap swap swap defaults  0 0" >> /etc/fstab

分区虽然启用,但只是临时的设置,重启之后就又没了。要想每次都挂载,需要修改/etc/fstab文件,这个文件在系统启动时被读取,里面的每一行记录了需要挂载的分区。把刚刚设置好的分区加进去,这样系统在启动时就能自动挂载SWAP分区了。

4. 总结 #

设置了SWAP分区后再次启动Tomcat,应用已经不会被系统关掉了,彻底解决了这个问题,当然由于SWAP是存储在硬盘上,与真正的内存相比速度差了很多,有条件最好还是升级内存。如果自己的系统上没有SWAP分区那就赶紧设置一下吧,以防出现进程莫名其妙关闭的问题。

评论加载中