我的ThreadPoolExecutor泄漏了内存吗?

Cla*_*den 8 java threadpoolexecutor

我正在使用a ThreadPoolExecutor来运行任务.后端是一个SynchronousQueue,所以如果执行程序已经执行了一个任务,它会抛出一个任务RejectedExecutionException.这是一个简单的测试用例:

public class ExecutorTest {

  final static Worker worker = new Worker();

  public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>());

    while (true) {
        try {                
            executor.execute(worker);                
        }catch (RejectedExecutionException e) {                
        }
    }        
  }

  static class Worker implements Runnable {

    private int i = 0;
    private long start = System.currentTimeMillis();

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(++i + " " + (System.currentTimeMillis() - start));
        } catch (InterruptedException ex) {                
        }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

预期的变化是:执行工作者并在睡眠一秒后打印出i(表示工人到目前为止执行的频率)以及自工人创建以来的毫秒数.所以我期待:

1 1015 
2 2015
3 3016 
4 4017
Run Code Online (Sandbox Code Playgroud)

这工作正常一段时间,但几乎在一小时后:

2919 2922196
2920 2942951
2921 2990407
Run Code Online (Sandbox Code Playgroud)

因此,一个工人执行与下一个工人执行之间的时间量是20秒(2919-> 2920)和38秒(2920-> 2921)等等.一切都变得非常缓慢,jvm花了很多时间在垃圾收集.最后(几天后)我遇到了OutOfMemoryError.

我在64位Linux机器上的Oracle JVM 1.7.0_07上使用-Xmx8M运行它(我假设后面会有更多堆空间出现效果).我很欣赏任何指针,但可能我只是错过了显而易见的事情.

小智 2

您可以尝试修改ThreadPoolExecutor实例化。您只需向构造函数添加一个参数即可使用RejectExecutionHandler它将默默地丢弃被拒绝的任务。

public static void main(String[] args) {
  ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());

  while (true) {
    executor.execute(worker);                
  }
}
Run Code Online (Sandbox Code Playgroud)

因此,如果你的问题来自重复RejectedExecutionException(我认为是这样),你就会避免它。

  • +1 这大大减少了 GC 活动量,如 `-verbose:gc -XX:+PrintGCDetails` 所示 (2认同)