标题:centos系统buff/cache占用过多内存导致java进程闪退

发表于

现象:
我的系统环境是centos + nginx + php + mysql + java,已经持续运行了450多天没有重启过。加上以前用1GB内存的云主机,几年来都有JAVA进程自动闪退的老毛病。当时考虑是内存不足,所以就降低nginx,php,mysql的内存占用量,但是没用效果,所以认为是物理内存1GB确实太小,怎么调优都不够使用。

后来换了2GB内存的云主机,问题依然存在,所以怀疑是java代码有问题,造成了内存溢出。因为不是很重要,所以也没有专门对JAVA程序代码进行调试。

直到今天,无意间发现了原因:
centos中内存的分配是buff/cache + free + used=物理内存大小,系统分配给临时文件系统的大小默认是用掉一半的物理内存,这样会造成buff/cache很大,而free很小,以致不够java分配到足够的内存,java进程又不能让buff/cache释放出一些空间,所以就崩溃了。

分析:
1、系统已经运行了450多天,除了java不稳定外,其他没有任何问题,nginx,php和mysql表现很好。
2、启动nginx,php,mysql,再启动tomcat,运行java,注意观察top命令的输出变化。现象是java所占内存会一直增长,一般到占用380MB时,进程就闪退了。
看/var/log/messages日志,有提示Out of memory: Kill process 13366 (java) score 183 or sacrifice child。
但是分析下,mysql占用不到300MB,php-fpm占用300MB左右,就算是java占用400MB,物理内存2GB也足够用了,为什么会闪退呢?
继续看top命令的输出变化,没启动java时,free是300MB左右,java运行后free就迅速减少,然后java闪退,free变大,把JAVA_OPTS占用内存的数量调到很小,也没用,看来无法严格控制java占用内存不超过一个数值。那么为什么free会这么小呢?继续观察和分析,得到主要原因是buff/cache占用了1GB左右的空间。
3、参考资料https://blog.csdn.net/u014740338/article/details/66975550,用命令echo 3 > /proc/sys/vm/drop_caches释放buff/cache空间,没有作用。
4、用命令free -h和cat /proc/meminfo查看结果,发现SUnreclaim很大,SReclaimable很小,几乎全是不可回收空间,所以上边的命令echo 3 > /proc/sys/vm/drop_caches也起不到作用。又尝试两个命令less /proc/slabinfo 和slabtop,依然没有作用。
5、继续查找资料https://www.v2ex.com/t/278921,认识到这部分空间是没办法回收的,只有重新定义tmpfs的大小,重启主机。

解决:
1、创建swap文件分区,占用硬盘1GB,限定tmpfs /dev/shm临时文件系统,占用内存大小为512MB
# df -h
# dd if=/dev/zero of=/root/swapfile bs=1M count=1024
# mkswap /root/swapfile
# swapon /root/swapfile
# vi /etc/fstab 添加如下内容:
tmpfs /dev/shm tmpfs defaults,size=512M 0 0
tmpfs /run tmpfs defaults,size=512M 0 0
/root/swapfile swap swap defaults 0 0

# umount /dev/shm
# mount /dev/shm
# reboot

2、整体内存性能调优,详见:http://pony.hk/?p=422