thrift 内存溢出
短信服务,运用了thrift框架。
thrift,是 Facebook 实现的一种高效的、支持多种编程语言的远程服务调用的框架。而推出最初,其存在很多问题,见以下链接
http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/ thrift学习入门
http://yanyiwu.com/work/2014/10/17/thrift-source-code-illustration.html thrift源码解析
http://blog.csdn.net/shijun_zhang/article/details/6863836 thrift rpc 使用常见问题解答和经验
所以,thrift的client最好是以连接池来进行管理,防止过多的client同时请求带来的问题。而thrift的server的选择,则要根据要提供的服务进行选择。
对应的server(java版)主要有
1、TSimpleServer 一般仅作为测试用
2、TNonblockingServer 非阻塞式,用nio进行通信,但依然只有一个进程处理请求
3、THsHaServer 混合模式,半同步/半异步。它使用一个单独的线程来处理网络I/O,一个独立的worker线程池来处理消息。
4、TThreadedSelectorServer 允许你用多个线程来处理网络I/O。它维护了两个线程池,一个用来处理网络I/O,另一个用来进行请求的处理。当网络I/O是瓶颈的时候, TThreadedSelectorServer比THsHaServer的表现要好
5、TThreadPoolServer 类似于线程池了,有一个专用的线程用来接受连接。一旦接受了一个连接,它就会被放入ThreadPoolExecutor中的一个worker线程里处理。但是worker线程被绑定到特定的客户端连接上,直到它关闭。
具体区别,请查看
http://blog.csdn.net/azhao_dn/article/details/8898610
http://m.blog.csdn.net/blog/hjx_1000/42779915
我们的短信服务,因为多了一家运营商,而且又分生产跟营销不同的渠道,再考虑到可扩展性,即后期会加入别的运营商,所以将短信服务进行了简单的重构。
提供两个接口,发送营销短信、发送生产短信,然后根据传入的cpid(标志着发送短信的场合),找到对应的渠道信息,再找到对应的operator,从而调用该operator对应的发送短信的方法。
未进行重构前,还一直没关注其占用内存情况,也没有报内存溢出。
重构后,一开始还比较稳定,用了几天之后,出现了内存溢出,一直到现在,出现的越来越频繁。
用 ps -aux | grep sms 查看该服务占用内存一直增加。即使gc后,也依然减少的很少。
用 jmap -histo pid > jvm.log,打印出内存中相关的内容,
num #instances #bytes class name
----------------------------------------------
1: 2179572 34873152 java.lang.Object
2: 63701 33237280 [C
3: 242114 29053680 java.net.SocksSocketImpl
4: 241711 27071632 sun.nio.ch.SocketChannelImpl
5: 241711 11602128 sun.nio.ch.SocketAdaptor
6: 30834 7792856 [B
7: 242115 7747680 java.net.Inet4Address
8: 241712 7734784 [Ljava.nio.channels.SelectionKey;
9: 45748 6613624 <constMethodKlass>
10: 45748 6231856 <methodKlass>
11: 242580 5821920 java.io.FileDescriptor
12: 241712 5801088 java.net.InetSocketAddress
13: 3993 4316064 <constantPoolKlass>
14: 242696 3883136 java.util.concurrent.atomic.AtomicInteger
15: 241712 3867392 sun.nio.ch.OptionAdaptor
16: 241712 3867392 sun.nio.ch.SocketOptsImpl$IP$TCP
17: 241711 3867376 sun.nio.ch.SocketChannelImpl$1
18: 241264 3860224 java.nio.channels.spi.AbstractInterruptibleChannel$1
19: 69769 3599896 <symbolKlass>
20: 16811 3220440 [I
除了socket相关的,基本都是char,String,int等数组之类的。
查询内存情况的相关工具介绍,参考此链接 http://blog.csdn.net/zhujiongming/article/details/8510462
用jstat -gc pid,关注gc情况,偶然间抓住了gc前后的jvm.log,进行对比后,发现socket相关的对象基本没有减少,初步怀疑thrift基于socket通信,没有关闭socket?但是后台是一个线程池,不解。还需要对thrift原理一级socket通信原理进行学习。
后来在本地跑thrift server,没有任何请求,占用内存如下图所示,顶峰时甚至达到过400-500M,而且,测试机上的thrift server也内存溢出过,请求量几乎没有。
thrift一直再监听是否有请求,占用大量内存?
可以选择jdk自带的jconsole以及jvirtualvm来监控内存使用情况,jvirtualvm可以dump出当时内存的内容,可以分析看到里面具体的信息,比如String是什么内容等。但是依然没有收获。
后来选用jprofiler,远程监控服务器,需要加入如下配置-agentpath:/data/jprofiler9/bin/linux-x64/libjprofilerti.so=port=8849,这样就可以通过8849端口,用jprofiler客户端远程监控服务器的内存情况了。
我们也可以加入gc日志,分析gc情况 -Xloggc:/data/logs/sms/gclogs/gc.log
加入堆内存溢出时,导出dump文件,保留当时内存内容 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/sms/outofmemory.hprof
jvm参数设置以及GC策略,参考此链接 http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html ,后期需要深入学习一下。
后来发现,报内存溢出时,400M的内存,只是占用了几十M?目前问题依然没有找到。