<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have searched the <a href="https://github.com/apache/dubbo/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I have checked the <a href="https://github.com/apache/dubbo/blob/master/FAQ.md">FAQ</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Dubbo version: 2.7.x</li>
<li>Operating System version: *</li>
<li>Java version: *</li>
</ul>
<h3 dir="auto">Steps to reproduce this issue</h3>
<p dir="auto">由AbstractProxyProtocol创建的invoker在调用destroy()销毁后，因为其destroy方法并不会清理资源，所以AbstractProtocol.invokers依然会持有该invoker的引用（invoker创建时会被添加到AbstractProtocol.invokers中）。</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="protected &lt;T&gt; Invoker&lt;T&gt; protocolBindingRefer(final Class&lt;T&gt; type, final URL url) throws RpcException {
        final Invoker&lt;T&gt; target = proxyFactory.getInvoker(doRefer(type, url), type, url);
        // AbstractInvoker.destroy方法只会更新isDestroyed字段，不会清理资源
        Invoker&lt;T&gt; invoker = new AbstractInvoker&lt;T&gt;(type, url) {
            @Override
            protected Result doInvoke(Invocation invocation) throws Throwable {
                try {
                    Result result = target.invoke(invocation);
                    // FIXME result is an AsyncRpcResult instance.
                    Throwable e = result.getException();
                    if (e != null) {
                        for (Class&lt;?&gt; rpcException : rpcExceptions) {
                            if (rpcException.isAssignableFrom(e.getClass())) {
                                throw getRpcException(type, url, invocation, e);
                            }
                        }
                    }
                    return result;
                } catch (RpcException e) {
                    if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) {
                        e.setCode(getErrorCode(e.getCause()));
                    }
                    throw e;
                } catch (Throwable e) {
                    throw getRpcException(type, url, invocation, e);
                }
            }
        };
       // invoker会保存所有新建的invoker
        invokers.add(invoker);
        return invoker;
    }"><pre class="notranslate"><code class="notranslate">protected &lt;T&gt; Invoker&lt;T&gt; protocolBindingRefer(final Class&lt;T&gt; type, final URL url) throws RpcException {
        final Invoker&lt;T&gt; target = proxyFactory.getInvoker(doRefer(type, url), type, url);
        // AbstractInvoker.destroy方法只会更新isDestroyed字段，不会清理资源
        Invoker&lt;T&gt; invoker = new AbstractInvoker&lt;T&gt;(type, url) {
            @Override
            protected Result doInvoke(Invocation invocation) throws Throwable {
                try {
                    Result result = target.invoke(invocation);
                    // FIXME result is an AsyncRpcResult instance.
                    Throwable e = result.getException();
                    if (e != null) {
                        for (Class&lt;?&gt; rpcException : rpcExceptions) {
                            if (rpcException.isAssignableFrom(e.getClass())) {
                                throw getRpcException(type, url, invocation, e);
                            }
                        }
                    }
                    return result;
                } catch (RpcException e) {
                    if (e.getCode() == RpcException.UNKNOWN_EXCEPTION) {
                        e.setCode(getErrorCode(e.getCause()));
                    }
                    throw e;
                } catch (Throwable e) {
                    throw getRpcException(type, url, invocation, e);
                }
            }
        };
       // invoker会保存所有新建的invoker
        invokers.add(invoker);
        return invoker;
    }
</code></pre></div>
<p dir="auto">而DobboProtocol创建的invoker销毁时则会清除invokers中的引用：</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="public void destroy() {
        // in order to avoid closing a client multiple times, a counter is used in case of connection per jvm, every
        // time when client.close() is called, counter counts down once, and when counter reaches zero, client will be
        // closed.
        if (super.isDestroyed()) {
            return;
        } else {
            // double check to avoid dup close
            destroyLock.lock();
            try {
                if (super.isDestroyed()) {
                    return;
                }
                super.destroy();
                // 从invokers中删除
                if (invokers != null) {
                    invokers.remove(this);
                }
                for (ExchangeClient client : clients) {
                    try {
                        client.close(ConfigurationUtils.getServerShutdownTimeout());
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }

            } finally {
                destroyLock.unlock();
            }
        }
    }"><pre class="notranslate"><code class="notranslate">public void destroy() {
        // in order to avoid closing a client multiple times, a counter is used in case of connection per jvm, every
        // time when client.close() is called, counter counts down once, and when counter reaches zero, client will be
        // closed.
        if (super.isDestroyed()) {
            return;
        } else {
            // double check to avoid dup close
            destroyLock.lock();
            try {
                if (super.isDestroyed()) {
                    return;
                }
                super.destroy();
                // 从invokers中删除
                if (invokers != null) {
                    invokers.remove(this);
                }
                for (ExchangeClient client : clients) {
                    try {
                        client.close(ConfigurationUtils.getServerShutdownTimeout());
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }

            } finally {
                destroyLock.unlock();
            }
        }
    }
</code></pre></div>
<p dir="auto">Pls. provide [GitHub address] to reproduce this issue.</p>
<h3 dir="auto">Expected Result</h3>
<p dir="auto">当invoker.destroy方法调用后，则该invoker应该从AbstractProtocol.invokers中remove掉（例如DubboInvoker的做法）。</p>
<h3 dir="auto">Actual Result</h3>
<p dir="auto">由于destroy方法中未将invoker从AbstractProtocol.invokers中删除，则会导致销毁的invoker无法被GC，内存泄漏</p>
<p dir="auto">If there is an exception, please attach the exception trace:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Just put your stack trace here!"><pre class="notranslate"><code class="notranslate">Just put your stack trace here!
</code></pre></div>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have searched the <a href="https://github.com/apache/dubbo/issues">issues</a> of this repository and believe that this is not a duplicate.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> I have checked the <a href="https://github.com/apache/dubbo/blob/master/FAQ.md">FAQ</a> of this repository and believe that this is not a duplicate.</li>
</ul>
<h3 dir="auto">Environment</h3>
<ul dir="auto">
<li>Dubbo version: 2.7.8</li>
<li>Operating System version: macOs Catalina 10.15.7</li>
<li>Java version: 1.8.0_231</li>
</ul>
<h3 dir="auto">Steps to reproduce this issue</h3>
<ol dir="auto">
<li>
<p dir="auto">config two registries in a dubbo project and set one(zk1) of the two to be default<br>
`<br>
&lt;dubbo:registry id="zk1" address="zookeeper://localhost:2181" default="true"/&gt;</p>
<p dir="auto">&lt;dubbo:registry id="zk2" address="zookeeper://localhost:2182"/&gt;<br>
`</p>
</li>
<li>
<p dir="auto">export  a service through @DubboService without config the registry<br>
`<br>
@DubboService(version = "1.0.0")<br>
public class GreetingServiceImpl implements org.apache.dubbo.springboot.api.GreetingService {</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="  public String sayHello(String name) {
       System.out.println(&quot;greeting service received: &quot; + name);
      return &quot;hello, &quot; + name;
  }"><pre class="notranslate"><code class="notranslate">  public String sayHello(String name) {
       System.out.println("greeting service received: " + name);
      return "hello, " + name;
  }
</code></pre></div>
<p dir="auto">}`</p>
</li>
</ol>
<h3 dir="auto">Expected Result</h3>
<p dir="auto">the service without config registry should only be registered to the default registry(zk1)  and not registered to annother registry(zk2)</p>
<h3 dir="auto">Actual Result</h3>
<p dir="auto">What actually happens?<br>
the service was registered to both two registries<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/12069629/97778051-79b40600-1baf-11eb-84a3-8dfc19ab2683.png"><img src="https://user-images.githubusercontent.com/12069629/97778051-79b40600-1baf-11eb-84a3-8dfc19ab2683.png" alt="image" style="max-width: 100%;"></a><br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/12069629/97778047-6e60da80-1baf-11eb-90fb-c506ae3a13f0.png"><img src="https://user-images.githubusercontent.com/12069629/97778047-6e60da80-1baf-11eb-90fb-c506ae3a13f0.png" alt="image" style="max-width: 100%;"></a></p>