我们有个与外部交互的接口是采用netty http,具体版本netty-4.1.18,为什么使用这个版本,我也不知道,历史原因。
由于netty都是异步请求,所以与外部交互总有些唯一的业务标识需要保存,以便前后数据可以勾兑。
这里先说明下,netty里的ByteBuf在读取channelRead未进行写write操作时,需要自己释放release。这和本次Error关系不大,继续说重点。
查看日志,首先发现了OutOfDirectMemoryError错误,这个错误也是间断性的出现,显然是内存不够用了,不管是heap还是direct。
直接dump了一份堆内存日志,具体命令:jmap -dump:format=b,file=tomcat.dump pid。我们的服务是放在tomcat上的,jps一下,那个bootStrap就是。
dump以后,找工具查看,推荐使用java自带的jvisualvm,在java目录的bin下,java自带的命令还是很好用的,比如上面提到的jps,jps -v比ps -ef |grep XXX 方便多了。
装载dump文件后,如图所示:
大概瞅一眼,某些类的实例也忒多了,满世界都是ConcurrentHashMap,绝壁有问题,这么造内存够用才怪,接着看到下面的netty有关的类实例,
查看代码使用AttributeKey的地方,又看了源码实现,大概有些眉目,应该是对象创建过多,导致内存溢出了。
每个实例下都挂这一个ConcurrentHashMap,不知道是不是我们的用法有问题,先不管了,先解决问题,
发现代码每次请求都创建一个连接channel,每次都会全局保存一个AttributeKey,以便在请求异步响应时,勾兑原始数据,so,每天几百万请求就有点春困秋乏了,
借用AttributeKey的思路,改为自己全局保存一个ConcurrentHashMap,然后键值对保存唯一业务标识,并且在异步处理之后remove过时数据。
AttributeKey我翻了翻源码,真没看到在哪里可以释放的地方,有一个set(null)当然代码并没有使用过后设空操作,但是AttributeKey内部的haspMap不会自动释放。
修改以后,重新部署。观察了几天,dump了新的内存日志,并没有发现过多的对象实例。继续观察一段时间。
有关内存溢出的问题其实并没有想的那么困难,内存不够用自然会FullGC或者内存溢出,有些内存泄漏也会最终引发FullGC或内存溢出,找到过多的垃圾对象,
该释放的释放,该单例的使用单例,能重用的尽量重用,应该没什么问题,看看dump文件基本就找到问题根源了。
原文地址:https://www.cnblogs.com/render-inside/p/9015891.html