Dubbo存在内存泄漏
[前提]
本文阐述的内容基于Dubbo 2.7.3版本
[正文]
如上图, 在上一篇 com.alibaba.fastjson存在内存泄漏 文章中, 我们解释了线程的threadLocals中存在内存泄漏的情况, 仔细观察上图, 还有一个地方, 在threadLocalMap属性的内部也存在662.19KB的内存, 这个地方也不正常.
查看threadLocalMap的内部属性
在org.apache.dubbo.rpc.FutureContext内部的result属性`持有`651.93KB内存, 这个result的内容实际是Dubbo接口的返回值. 而这个FutureContext对象也是在调用外部Dubbo接口的时候创建的.
我们简单分析下一个业务线程调用Dubbo接口的过程.
当业务线程需要调用外部Dubbo接口的时候, 会创建一个DefaultFuture, 每个DefaultFuture对象都会有唯一的一个Id与之对应, 并把这个关系放到Map中
private DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
this.id = request.getId();
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(TIMEOUT_KEY, DEFAULT_TIMEOUT);
// 存储 id <-> DefaultFuture关系
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
由于接口调用都会有超时, 那么如何实现这个超时机制呢?
将一个超时任务放入到时间轮上.
// org.apache.dubbo.remoting.exchange.support.DefaultFuture#newFuture
public static DefaultFuture newFuture(Channel channel, Request request, int timeout) {
final DefaultFuture future = new DefaultFuture(channel, request, timeout);
// 超时检查
timeoutCheck(future);
return future;
}
// org.apache.dubbo.remoting.exchange.support.DefaultFuture#timeoutCheck
private static void timeoutCheck(DefaultFuture future) {
TimeoutCheckTask task = new TimeoutCheckTask(future.getId());
future.timeoutCheckTask = TIME_OUT_TIMER.newTimeout(task, future.getTimeout(), TimeUnit.MILLISECONDS);
}
在我之前的 Netty中的时间轮(v3.10.7) 文章中介绍了时间轮, 通过时间轮的方式, 检测任务是否超时到期了.
接下来就是将DefaultFuture等信息组装成一个FutureContext放入到线程的ThreadLocalMap中.
// org.apache.dubbo.rpc.protocol.dubbo.DubboInvoker#doInvoke
protected Result doInvoke(final Invocation invocation) throws Throwable {
try {
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodPositiveParameter(methodName, TIMEOUT_KEY, DEFAULT_TIMEOUT);
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
return AsyncRpcResult.newDefaultAsyncResult(invocation);
} else {
AsyncRpcResult asyncRpcResult = new AsyncRpcResult(inv);
CompletableFuture