说在前面的话
朋友,你经历过部署好的服务突然内存溢出吗?
你经历过没有看过Java虚拟机,来解决内存溢出的痛苦吗?
你经历过一个BUG,百思不得其解,头发一根一根脱落的烦恼吗?
我知道,你有过!
但是我还是要来说说我的故事..................
背景:
有一个项目做一个系统,分客户端和服务端,客户端用c++写的,用来收集信息然后传给服务端(客户端的数量还是比较多的,正常的有几千个),
服务端用Java写的(带管理页面),属于RPC模式,中间的通信框架使用的是thrift。
thrift很多优点就不多说了,它是facebook的开源的rpc框架,主要是它能够跨语言,序列化速度快,但是他有个不讨喜的地方就是它必须用自己IDL来定义接口
thrift版本:0.9.2.
问题定位与分析
步骤一.初步分析
客户端无法连接服务端,查看服务器的端口开启状况,服务端口并没有开启。于是启动服务端,启动几秒后,服务端崩溃,重复启动,服务端依旧在启动几秒后崩溃。
步骤二.查看服务端日志分析
分析得知是因为java.lang.OutOfMemoryError: Java heap space(堆内存溢出)导致的服务崩溃。
客户端搜集的主机信息,主机策略都是放在缓存中,可能是因为缓存较大造成的,但是通过日志可以看出是因为Thrift服务抛出的堆内存溢出异常与缓存大小无关。
步骤三.再次分析服务端日志
可以发现每次抛出异常的时候都会伴随着几十个客户端在向服务端发送日志,往往在发送几十条日志之后,服务崩溃。可以假设是不是堆内存设置的太小了?
查看启动参数配置,最大堆内存为256MB。修改启动配置,启动的时候分配更多的堆内存,改成java -server -Xms512m -Xmx768m。
结果是,能坚持多一点的时间,依旧会内存溢出服务崩溃。得出结论,一味的扩大内存是没有用的。
**为了证明结论是正确的,做了这样的实验:**
> 内存设置为256MB,在公司服务器上部署了服务端,使用Java VisualVM远程监控服务器堆内存。
>
> 模拟客户现场,注册3000个客户端,使用300个线程同时发送日志。
>
> 结果和想象的一样,没有出现内存溢出的情况,如下图:
> 上图是Java VisualVM远程监控,在压力测试的情况下,没有出现内存溢出的情况,256MB的内存肯定够用的。
步骤四.回到thrift源码中,查找关键问题
服务端采用的是Thrif