Servlet“已启动一个线程但未能停止它”-Tomcat 中的内存泄漏

Posted

技术标签:

【中文标题】Servlet“已启动一个线程但未能停止它”-Tomcat 中的内存泄漏【英文标题】:Servlet "has started a thread but failed to stop it" - memory leak in Tomcat 【发布时间】:2014-09-27 10:00:06 【问题描述】:

Apache Tomcat 说了很多遍:

Web 应用程序 [/MyServlet] 似乎启动了一个名为 [pool-61-thread-2] 的线程,但未能停止它。这很可能会造成内存泄漏。

这很危险吗? servlet 应该能够每天处理 10.000 个请求。 完成后如何关闭线程?

class Worker 

        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        private final int threadNumber;

        Worker(
                CountDownLatch startSignal,
                CountDownLatch doneSignal,
                int threadNumber
        )

            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
            this.threadNumber = threadNumber;

        

        public String[][] getSomeStrArrArr() 

            String[][] isRs = new String[8][20];
            String[][] inRs = new String[8][20];
            String[][] iwRs = new String[8][20];

            try 

                startSignal.await();

                if (threadNumber == 1) 
                    // get String[][] result for thread number 1
                    isRs = getIS(erg1, erg2, request);

                

                if (threadNumber == 2) 
                    // get String[][] result for thread number 2
                    inRs = getIN(search_plz, request);
                

                if (threadNumber == 3) 
                    // get String[][] result for thread number 3
                    iwRs = getIW(erg1, erg2, request);
                

                doneSignal.countDown();

             catch (InterruptedException ex) 

                System.out.println(
                        "Thread number "+threadNumber+" has been interrupted."
                );

            
            if (threadNumber == 1) 
                return isRs;
            
            if (threadNumber == 2) 
                return inRs;
            
            if (threadNumber == 3) 
                return iwRs;
            
            return null;
        


        public Callable<String[][]> getSomeCallableStrArrArr()
            return new Callable<String[][]>() 
                public String[][] call() throws Exception 
                    return getSomeStrArrArr();
                
            ;
        

    

    ExecutorService pool = Executors.newFixedThreadPool(3);
    Set<Future<String[][]>> set = new HashSet<Future<String[][]>>();
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(3);
    for (int i=1;i<=3;i++) 
        Worker worker = new Worker(startSignal,doneSignal,i);
        Callable<String[][]> callable =
                worker.getSomeCallableStrArrArr();
        Future<String[][]> future = pool.submit(callable);
        set.add(future);
    
    startSignal.countDown();
    try 
        doneSignal.await();

【问题讨论】:

通常你不应该在多线程环境中初始化新线程,而是让应用服务器处理它们,除非你知道你在做什么。我们需要更多信息来更好地了解您的问题,或者使用像 Eclipse Memory Analyzer 这样的分析器来检查内存泄漏的位置。 干净的重新部署在Tomcat(不仅仅是Tomcat)中是一件非常脆弱的事情。重新启动服务器通常是不可避免的,然后泄漏就不是问题了。 抱歉,您的意思是什么以及如何避免这种情况? 【参考方案1】:

是的,这是个问题。如果您的代码启动非守护线程,那么这些线程将继续工作,直到它们退出它们的运行方法。即使其他一切都完成了,旧的 JVM 也会在这些线程继续运行时挂起。如果您启动一个新实例,那么您可能会遇到旧线程仍在与新实例创建的线程一起工作的情况。

需要对任务进行设计,以便它们能够响应中断(而不是吃掉异常并继续,这就是您的示例所示)。这意味着检查当前线程上的中断标志,并以一种有用的方式捕获 InterruptedException,允许任务中断其工作并在需要时重置中断标志。 ExecutorService 实现有一个 shutdownNow 方法,它会中断当前任务。

Here's an example of how to stop a thread using interruption.

确保执行器被关闭,you can handle this in a ServletContextListener。

【讨论】:

在我的情况下,线程来自可调用,不可运行,因为我需要从每个线程返回。这也适用于中断吗?我需要返回,但不知道线程何时返回...... @user3876178:您的示例不完整,您目前是否正在关闭您的池? @user3876178:ExecutorService.shutdownNow。如果任务对中断做出响应,这会有所帮助。 简单但有效!我希望我能工作,但我没有得到任何错误了。谢谢!您是否对此有所了解:“Web 应用程序 [/MyServlet] 似乎已经启动了一个名为 [mysql Statement Cancellation Timer] 的线程,但未能停止它。这很可能会造成内存泄漏。”我只是有一些正常的mysql调用,没什么特别的...... 谢谢 Nathan,我有旧的 5.0.8 mysql 连接器。更新到最新版本并解决了问题/错误!

以上是关于Servlet“已启动一个线程但未能停止它”-Tomcat 中的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

maven构建web项目,cannot be cast to javax.servlet.Servlet

maven中的cannot be cast to javax.servlet.Filter怎么解决

cannot be cast to javax.servlet.Filter 报错, 原因servlet-api.jar冲突

CharacterEncodingFilter cannot be cast to javax.servlet.Filter

maven2报xxxServlet cannot be cast to javax.servlet

maven2报xxxServlet cannot be cast to javax.servlet