Java ApacheAvro线程本地对象在Tomcat un deploy上徘徊

Java ApacheAvro线程本地对象在Tomcat un deploy上徘徊,java,tomcat,avro,Java,Tomcat,Avro,我们使用ApacheAvro作为Python应用程序和在Tomcat服务中运行的一些第三方Java库之间的JSON接口。我们决定简单地扩展org.apache.avro.ipc.ResponderServlet类来实现我们自己的servlet。servlet非常简单,因为它在构造函数中实例化ResponderServlet超类,并重写init()和destroy()方法,以便为我们在servlet中运行的第三方库做一些内部管理 然而,当Tomcat取消部署我们的webapp时,我们会看到一些严重

我们使用ApacheAvro作为Python应用程序和在Tomcat服务中运行的一些第三方Java库之间的JSON接口。我们决定简单地扩展org.apache.avro.ipc.ResponderServlet类来实现我们自己的servlet。servlet非常简单,因为它在构造函数中实例化ResponderServlet超类,并重写init()和destroy()方法,以便为我们在servlet中运行的第三方库做一些内部管理

然而,当Tomcat取消部署我们的webapp时,我们会看到一些严重错误,警告ThreadLocal相关的内存泄漏

SEVERE: The web application [/hotwire] created a ThreadLocal with key of type [org.apache.avro.Schema$3] (value [org.apache.avro.Schema$3@4464784f]) and a value of type [java.lang.Boolean] (value [true]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
Jan 24, 2013 2:19:36 AM org.apache.catalina.loader.WebappClassLoader checkThreadLocalMapForLeaks
SEVERE: The web application [/hotwire] created a ThreadLocal with key of type [org.apache.avro.generic.GenericDatumReader$1] (value [org.apache.avro.generic.GenericDatumReader$1@2016ad9d]) and a value of type [org.apache.avro.util.WeakIdentityHashMap] (value [org.apache.avro.util.WeakIdentityHashMap@30e02ee0]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
我们可能在某个地方做了一些幼稚的事情,因为我们在web上找不到任何有关此场景的帮助。尽管如此,我们还是希望有人能告诉我们哪里出了问题

下面是对我们的servlet的一瞥

public class HotWire extends ResponderServlet{
    public HotWire() throws IOException
    {
              super(new SpecificResponder(Engine.class, new EngineImpl()));
    }

    @Override
        public void init() {
        try {
         super.init();
                 try {
                      init_engine();                
                      } catch (EngineInitException e) {
                      e.printStackTrace();
                      }
            } catch (ServletException e) {
                 e.printStackTrace();
            }
        }

        @Override
        public void destroy() {
            super.destroy();
            shutdown_engine();
        }

        public static class EngineImpl implements EngineInterface  {
            public Boolean create(Parameters message) {
                Boolean status = null;
                try {
                    status = engine.create_object(message);
                } catch (SemanticException | IOException e) {
                    e.printStackTrace();
                }
                return status;
            }

}

我看到了一个类似的问题。如果您看这里,就会发现一个静态ThreadLocal缓存被填满,无法删除它:

private static final ThreadLocal RESOLVER\u CACHE=ThreadLocal
.withInitial(WeakIdentityHashMap::new);
在我的例子中,每个请求都有不同的模式,因此我的Spring引导服务最终会耗尽内存并终止

这也是类加载器泄漏的一个示例:

我可以在这里看到JIRA要求改进的请求:

更新:我能够解决内存泄漏的问题。有一个ExecutorService正在重新使用线程。现在我关闭服务,这会导致线程被最终确定。这允许对线程本地内存进行GC'd

正如有人指出的,解析器_缓存使用了一个WeakIdentityHashMap,它应该允许缓存被GC’d,但我没有这样做

  private static final ThreadLocal<Map<Schema, Map<Schema, ResolvingDecoder>>> RESOLVER_CACHE = ThreadLocal
      .withInitial(WeakIdentityHashMap::new);