简体   繁体   English

重复超时 Java Future 导致 JVM 用完 memory

[英]Repeated timeouts with Java Future causes JVM to run out of memory

Our Java application is having an issue where it blocks indefinitely when it tries to write to a log file located on a NFS share and the NFS share is down.我们的 Java 应用程序有一个问题,当它尝试写入位于 NFS 共享上的日志文件并且 NFS 共享已关闭时,它会无限期地阻塞。

I was wondering whether we could solve this problem by having a Future execute the write operation with a timeout.我想知道我们是否可以通过让 Future 超时执行写操作来解决这个问题。 Here is a little test program I wrote:这是我写的一个小测试程序:

public class write_with_future {
    public static void main(String[] args) {
        int iteration=0;
        while (true) {
            System.out.println("iteration " + ++iteration);

            ExecutorService executorService = Executors.newSingleThreadExecutor();
            Future future = executorService.submit(new Runnable() {
                public void run() {
                    try {
                        Category fileLogCategory = Category.getInstance("name");
                        FileAppender fileAppender = new FileAppender(new SimpleLayout(), "/usr/local/app/log/write_with_future.log");
                        fileLogCategory.addAppender(fileAppender);
                        fileLogCategory.log(Priority.INFO, System.currentTimeMillis());
                        fileLogCategory.removeAppender(fileAppender);
                        fileAppender.close();
                    }
                    catch (IOException e) {
                        System.out.println("IOException: " + e);
                    }
                }
            });

            try {
                future.get(100L, TimeUnit.MILLISECONDS);
            }
            catch (InterruptedException ie) {
                System.out.println("Current thread interrupted while waiting for task to complete: " + ie);
            }
            catch (ExecutionException ee) {
                System.out.println("Exception from task: " + ee);
            }
            catch (TimeoutException te) {
                System.out.println("Task timed out: " + te);
            }
            finally {
                future.cancel(true);
            }

            executorService.shutdownNow();
        }
    }
}

When I ran this program with a maximum heap size of 1 MB, and the NFS share was up, this program was able to execute over 1 million iterations before I stopped it.当我以 1 MB 的最大堆大小运行此程序并且 NFS 共享已启动时,此程序在我停止之前能够执行超过 100 万次迭代。

But when I ran the program with a maximum heap size of 1 MB, and the NFS share was down, the program executed 584 iterations, getting a TimeoutException each time, and then it failed with a java.lang.OutOfMemoryError error.但是当我运行最大堆大小为 1 MB 的程序并且 NFS 共享关闭时,程序执行了 584 次迭代,每次都出现 TimeoutException,然后失败并出现java.lang.OutOfMemoryError错误。 So I am thinking that even though future.cancel(true) and executorService.shutdownNow() are being called, the executor threads are blocked on the write and not responding to the interrupts, and the program eventually runs out of memory.所以我在想,即使future.cancel(true)executorService.shutdownNow()被调用,执行线程在写入时被阻塞并且不响应中断,程序最终用完 memory。

Is there any way to clean up the executor threads that are blocked?有没有办法清理被阻塞的执行线程?

If appears that Thread.interrupt() does not interrupt threads blocked in an I/O operation on an NFS file.如果似乎Thread.interrupt()不会中断在 NFS 文件上的 I/O 操作中阻塞的线程。 You might want check the NFS mount options, but I suspect that you won't be able to fix that problem.您可能想检查 NFS 挂载选项,但我怀疑您无法解决该问题。

However, you could certainly prevent it from causing OOME's.但是,您当然可以防止它导致 OOME。 The reason you are getting those is that you are not using ExecutorService s as they are designed to be used.你得到这些的原因是你没有使用ExecutorService ,因为它们是为使用而设计的。 What you are doing is repeatedly creating and shutting down single thread services.你所做的是重复创建和关闭单线程服务。 What you should be doing is creating on instance with a bounded thread pool and using that for all of the tasks.您应该做的是在实例上创建一个有界线程池并将其用于所有任务。 If you do it that way, if one of the threads takes a long time... or is blocked in I/O... you won't get a build-up of threads, and run out of memory. Instead, the backlogged tasks will sit in the ExecutorService 's work queue until one of the worker thread unblocks.如果你这样做,如果其中一个线程需要很长时间......或者在 I/O 中被阻塞......你不会得到线程的积累,并且用完 memory。相反,积压的任务将位于ExecutorService的工作队列中,直到其中一个工作线程解除阻塞。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM