未完成的CompletableFuture调用链是否会被垃圾回收器回收?
关于CompletableFuture垃圾回收相关问题解答
核心问题对应解答
常规无外部引用场景下,阻塞在
join()的线程和关联CompletableFuture会不会被GC回收?
你的判断正确,实际不会被回收。核心规则是:已经启动的Java线程本身属于GC Root,只要线程还存活(不管是运行中还是阻塞在join())就不会被回收。线程栈会持有内部所有CompletableFuture的引用,只要线程没退出,这些对象永远不会被GC,哪怕对应的CompletableFuture永远没有完成的可能。流程抛出异常没走到
join(),相关对象会不会被回收?
你的判断正确。线程抛出异常后会终止运行,不再是存活GC Root,栈帧销毁后所有内部CompletableFuture的引用都会被释放,只要没有其他外部引用持有这些对象,下次GC就会全部回收,不会产生内存泄漏。把内部
CompletableFuture暴露到外部引用,线程会不会在外部调用complete()之前被GC回收?
你的判断正确,实际不会发生。只要线程还存活且阻塞在join()上,就属于GC Root,JVM不会提前回收存活线程,Java语言规范也不允许这种优化,只有当线程执行完毕退出后才会被GC回收。
衍生问题解答
- 未启动的
CompletableFuture(手动new出来未调用complete的实例)挂载了thenApplyAsync()、anyOf()等方法会不会被回收?
只要没有GC Root持有该实例的引用,不管挂载了多少链式方法,都会被正常GC。链式调用只是在CompletableFuture内部建立引用关系,没有外部根引用的话整组对象都会被判定为垃圾。 - 调用链末尾存在
join()/get()的话,相关CompletableFuture会不会被回收?
只要join()/get()是在存活线程上调用的,线程会持有CompletableFuture的引用,不会被回收。如果调用阻塞方法的线程已经退出,就不会再影响对象回收。 - 确定不会启动
CompletableFuture链的话,推送毒丸是不是合理操作?
合理且有必要。主动调用completeExceptionally()传入自定义终止异常,能主动唤醒所有阻塞在join()/get()上的线程,避免线程一直阻塞导致的内存、线程泄漏。如果不做这个操作,万一有其他线程还在等待该CompletableFuture,就会一直卡到进程退出。 - 线程执行器什么时候会调度新任务?是不是只需要关注内存泄漏不用关注线程泄漏?
调度时机和你的判断一致:只有前一个CompletableFuture完成时,才会把后续链式异步任务提交给执行器。如果用的是公共线程池,确实不需要太担心线程泄漏,任务没提交就不会占用线程,顶多是CompletableFuture本身占用内存。但如果是自定义的专用线程、单线程池等场景,还是要注意阻塞线程的泄漏问题。
验证线程状态的方法
要区分线程是被join()阻塞还是线程池空闲阻塞,有两个简单方案:
- 抓线程栈:用jstack命令或者IDE调试器查看线程栈,如果是被
join()阻塞,会明确显示java.util.concurrent.CompletableFuture.join()的调用栈,线程状态为WAITING/TIMED_WAITING;如果是线程池空闲线程,栈里会显示java.util.concurrent.ThreadPoolExecutor.getTask()相关调用。 - 自定义线程名:创建线程池、业务线程时指定可区分的名称前缀,直接通过线程名就能判断归属。
内容的提问来源于stack exchange,提问作者Robin Kreuzer
相关产品推荐
相关产品推荐

