背景
最近写了个缓存程序,没有用docker部署,而是直接打了个jar包部署到linux服务器上。
过了几天,闹心的事情出现了,这个服务每隔多久就会莫名其妙的挂掉。
刚开始还以为是OOM导致服务不可用,然后打开服务日志查看后,发现一条报错都没有。。
闹心的事情出现了好几次,其实这个时候我猜测服务应该是被Linux服务器给kill掉了。不过因为是开发环境,自己也不想管这茬(我们开发服务器上,内存常年不足),直到预生产上也出现了一次,我才真正重视起来。
排查
后面看了几篇OOM-killer的文章,
感谢大佬分享
跟一下系统的相关日志
- 查看系统日志,查找“杀进程”有关的相关信息
egrep -i 'killed process' /var/log/messages
复制代码
- 查看内核日志
dmesg | grep java
复制代码
在茫茫的日志中,终于找到了我那个缓存服务被杀掉的信息,原来真的是被操作系统杀死的。
Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快的进程,然后防止内存耗尽而自动把该进程杀掉。内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参考内核源代码linux/mm/oom_kill.c,当系统内存不足的时候,out_of_memory()被触发,然后调用select_bad_process()选择一个”bad”进程杀掉。如何判断和选择一个”bad进程呢?linux选择”bad”进程是通过调用oom_badness(),挑选的算法和想法都很简单很朴实:最bad的那个进程就是那个最占用内存的进程。
其实定位到这里差不多就已经可以了,我的那个服务确实是比较吃内存,而我们开发服务器上,32G的内存十分紧缺。
那么最简单的方法就是加内存喽。。那我可以下班了嘛(╹▽╹)。。
但是,学过Java虚拟机的我,意识到并没有那么简单。
解决
部署的缓存服务,确实存在比较吃内存的情况,后续代码也优化掉了这点。
但是,最bad的那个进程就是那个最占用内存的进程,我的服务平时应该还不至于这么占内存啊!!
后来想到了JVM堆内存的默认配置
-Xms,表示堆的起始内存(新生代和老年代)全称为-XX:InitialHeapSize
X是JVM的运行参数,ms是memory start。
默认单位为字节,有k、m、g
-Xmx,表示堆的最大内存全称为-XX:MaxHeapSize
默认的堆空间大小:
-Xms = 计算机内存大小 /64
-Xmx = 计算机内存大小 /4
原来是我的最大堆内存没有设置,在默认的情况下,最高能占到服务器内存的四分之一。
那个缓存服务每隔一段时间会进行一次落库,在落库的时候内存占用会明显上升,在默认设置、极端情况下能达到8G,我们那个开发服务器显然没有那么多内存给我用,于是就被killed了。
为了不让它被killed,限制一下堆内存的大小,让他不够用的时候GC好了。
修改了原先的启动命令,加上了-Xmx和-Xms
nohup java -jar -Xms2g -Xmx2g -Dspring.profiles.active=dev35-master tcache-server.jar &
复制代码
在之后的一段时间,就再也没有看到我的服务被杀掉了。




近期评论