Java 垃圾收集似乎关闭了本地执行器并导致RejectedExecutionException

Java 垃圾收集似乎关闭了本地执行器并导致RejectedExecutionException,java,multithreading,garbage-collection,jvm,google-http-client,Java,Multithreading,Garbage Collection,Jvm,Google Http Client,间歇性头痛需要帮助。代码正在调用com.google.api.client.http.HttpRequest#executeAsync(),其基本逻辑如下: @Beta public Future<HttpResponse> executeAsync(Executor executor) { FutureTask<HttpResponse> future = new FutureTask<HttpResponse>(new Callable&l

间歇性头痛需要帮助。代码正在调用
com.google.api.client.http.HttpRequest#executeAsync()
,其基本逻辑如下:

  @Beta
  public Future<HttpResponse> executeAsync(Executor executor) {
    FutureTask<HttpResponse> future = new FutureTask<HttpResponse>(new Callable<HttpResponse>() {

      public HttpResponse call() throws Exception {
        return execute();
      }
    });
    executor.execute(future);
    return future;
  }

  @Beta
  public Future<HttpResponse> executeAsync() {
    return executeAsync(Executors.newSingleThreadExecutor());
  }
根据我的理解,GC不应该清理这个执行器,因为它还没有超出范围,但根据错误日志判断,这似乎就是行为

编辑:感谢您的快速回复。我可能需要澄清,我也不能随意复制这个,否则我可能会有更多的线索。代码通常运行良好,但这种错误很少发生,我们所拥有的只是我为症状粘贴的日志信息


我粘贴的代码不是我的。这是我正在使用的开源库
googlehtpjava客户端
。所以我没有办法改变这一点。我当然可以自己创建一个长期的
执行器
,并用第一个方法调用它,但我正在努力了解目前的问题是什么

返回的
ExecutorService
恰好是
ThreadPoolExecutor
包装在一个
FinalizableDelegatedExecutorService
中,这是一个关闭其包装的
ExecutorService
的实现详细信息。我们可以从您的日志中得知执行器已关闭

[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
但是,为什么要最终确定目标?你说

根据我的理解GC不应该清理这个执行者因为它 还没有超出范围,但根据错误日志判断 这似乎是一种行为

这是一种常见的误解。是一种编译时特性,用于确定在何处可以使用名称引用源代码中的某个实体。它与运行时垃圾收集无关

垃圾收集(和终结)由对象的可达性控制

当对象不再被引用时,可以由 垃圾收集器。如果对象声明终结器,则终结器为 在回收对象之前执行,以给对象最后一次 有机会清理否则不会释放的资源。 当不再需要某个类时,可以将其卸载

JVM不会对对象进行垃圾收集

可访问对象是可以从任何活动线程在任何可能的连续计算中访问的任何对象

正如(Oracle开发人员)对此问题的公认答案中所述

可达性分析允许

对于要最终确定和垃圾收集的对象,即使存在 在堆栈上的局部变量中引用它

您看到的是JVM做出了这样一个决定:通过从活动线程继续计算并完成它,不再能够访问
FinalizableDelegatedExecutorService
(及其
ThreadPoolExecutor
)。该操作将关闭执行器,并在提交任务时抛出
RejectedExecutionException

这是该实现中的一个已知问题,是为了讨论解决方案(本质上只是更好的文档)

如果由我决定,
googlehttp客户端
会将其实现更改为使用未使用
FinalizableDelegatedExecutorService
包装的实现。(我已经开始讨论图书馆的解决方案。)

我的建议是创建自己的
ExecutorService
(可能使用
newFixedThreadPool
)并使用重载的
executeAsync(Executor)
方法



问题已解决,请参阅。

什么关闭您的
执行器
?如果你向数千个休眠线程发送垃圾邮件,则很可能会出现问题。我无法使用此代码重现你的问题。在对Executor调用shutdown()后,你是否以某种方式提交了新任务?@SotiriosDelimanolis,我可能指的是生命周期,或者抱歉,也许我不擅长官方术语。我认为在本例中,一旦
executeAsync()
完成,执行器就可以自由地让GC执行(在执行器的终结器中调用shutdown)。但是当异常发生时,executor仍在使用中,因此GC绝对不应该触碰它并导致它关闭,对吗?@jwils请注意,这部分不是由我控制的,而是我正在使用的google http客户端。作为一个用户,我只需在我的代码中调用
executeAsync()
,所使用的执行器就完全在这个
HttpRequest
类中。我来寻求答案,得到了答案,还上了一堂小课。希望我能给你两个接受,谢谢!还有:“优化程序的转换可以设计为减少可访问对象的数量,使其少于那些天真地认为可访问的对象。[…]另一个例子是,如果对象字段中的值存储在寄存器中,则会出现这种情况。然后,程序可能会访问寄存器而不是对象,并且不再访问该对象。这意味着该对象是垃圾。[…]”顺便说一下,如果我事先知道只会安排一个作业,我宁愿使用
executeAsync(r->newthread(r.start())这意味着自动清理,而不存在提前完成的风险…是另一个抵消这些优化的选项
[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]