由一个服务器异常引发的思考 --JVM堆外内存


开始


前两天意外发现这么一个情况,某个组内共用的JAVA服务器上,莫名其妙的发生个别服务进程被杀死的情况,java服务都像蒸发了一样,莫名其妙的消失了,通过dmesg命令如下所示




通过dmesg命令可以发现,都是超过了系统内存,导致了Linux系统 杀掉了这几个java进程


Linux内存模型


 首先,我们来粗略地了解下Linux的内存模型,由于我们的Linux版本是x86的内存架构,这边我们仅针对x86来做分析


主要包含以下几个方面:


  • 虚拟内存管理,介于用户程序和物理内存之间的逻辑层,就是我们常说的用户态内存
  • 物理内存管理,内核级内存,一般由CPU才能调用
  • 内核虚拟内存,这个是内核态,用户映射数据到真实的内存空间的一个逻辑层,他可以接受来自用户的,也可以是来自内核的
  • 交换和缓存,参考操作系统的三级缓存架构,这不做赘述。


我们主要讨论这的虚拟内存管理,这也是Linux系统级别的主要实现。

什么样的东西会消耗内存?

代码,库,堆,栈等等,项目初始化,运行时这些都会去消耗内存。从操作系统级别的角度来说,申请出来的内存,只要没有真正被access,那么他就不算,因为他没有被分配到物理内存页面上去。


当然了,操作系统也会有一些防护拦截功能,比如说swap。

swap交互内存区域,他的作用是当内存不足的时候,会将磁盘虚拟成内存给进程使用,这会造成很大的问题,我们都知道,磁盘的读写速度是比较糟糕的,如果真的走到了swap分区,系统的性能将会直线下降

我们来看下服务器上swap的使用情况


由上图可见,swap分区已经被占用的满满的

我们再来看下swap的参数设置


swap的设置在0-100之间,越高内核对内存的回收便会更加积极。

当然了,可以扩容对swap分区,但这是没有必要的,这只会进一步降低系统的性能


由上可以看到,我们的系统内存已经炸了,直接将swap填满了已经。


JVM内存排查


 首先我们来看下我们这个服务器上的部署方案,在这个服务器上,我们混合部署了3个JAVA进程,每一个进程的启动脚本如下所示



由上能看到我们对每个进程分配128M的内存空间,而我们的系统内存为8G,粗略地看不出什么,我们再来看下jps来看下启动参数


实际分配的也是128M


再通过PS命令查看进程的内存和虚拟内存



由此可见,已经远远的超出了128,这我们可以知道他是有堆外内存的使用。


pmap


我们通过pmap命令来查看进程的内存分配,并且通过Rss排个序



太长了,我省略一部分,注意以上有个libJvm.so,这是应用程序大量申请内存块的一个的一个表现,但是这也不能告诉我具体的原因。


哎呀,有点技穷了,非专业运维,还好我们还有个超级工具,perf


perf


使用perf record -g -p 32071查看栈函数调用情况,执行一段时间后再执行 perf report -i perf.data 命令

我们看下输出



筛选下


可以看到,大量的文件读写操作


排查代码


发现,在此项目中,有个定时去某第三方文件服务器拉取语音文件,并且解析,再写入我们的文件服务器,并且写入数据库的操作

每一次拉取完,业务处理完,我们并没有对文件进行句柄闭合,导致文件一直处于读的状态处理方式,每次读取完成,删除他


经观察,问题解决,完美。


题外话


当碰到内存不足的时候,最最简单的方式,加内存,没有之一,排查只是后续优化操作。

暂无评论