Ephemeral threads

Alex Miller alex at puredanger.com
Thu Jan 8 17:48:43 UTC 2026


I’d like to discuss adding support for ephemeral threads in the API. I know
virtual threads were originally ephemeral but before the Java 21 release
virtual thread tracking was added for observability (
https://bugs.openjdk.org/browse/JDK-8309406). It seems the notion of
explicit ephemeral threads has been considered as a possible feature in the
past to bring that back (
https://mail.openjdk.org/pipermail/loom-dev/2024-July/006892.html).

In Clojure, we released a CSP/Go-like system called core.async way back in
2013 (https://clojure.github.io/core.async/rationale.html). This system is
implemented via inversion of control and does parking on channel (queue)
operations via code rewriting. Core.async go blocks are ephemeral - they
are represented as data, referred to by channels when the go block is
parked waiting to put or take data on those channels. If all of the
channels a go block may be waiting on are GC’ed, the go block is GC’ed as
it can never run again.

We have ported core.async to use virtual threads as it provides a superset
of the blocking/parking semantics we need (other than ephemerality). There
are many benefits to this change - no more code rewriting, lighter-weight
handoffs, extended parking support for I/O operations, etc. We would love
to make this switch.

However, due to virtual thread tracking the ephemeral thread patterns our
users have relied on for more than a decade now result in go blocks being
strongly tracked even when unreachable, which creates memory leaks. Setting
-Djdk.trackAllThreads=false does what we want, but we are uneasy about
relying on undocumented flags that could change or go away.


I understand the history of this change with respect to observability, and
the expectation that existing Java users are used to ensuring that threads
complete. Unlike Java users, our Clojure core.async users DO expect to be
able to safely abandon go blocks when all of their channels are no longer
reachable - we’ve been doing that for a long time.

Virtual threads open the possibility of new Erlang-like architectures.
Ephemeral threads importantly enable lightweight process *library*
development as these new patterns can be composed without needing to handle
the termination concerns. core.async e.g. provides pub/sub, multiplexing,
pipelining and various other bits of non-trivial plumbing as libraries.
Most of those run as infinite loops to which user code maintains no
enduring references. There is simply no way to build such libraries on an
old-school approach to process termination.

Given where things stood at Java 21 time and where we are now, can we move
the idea of ephemeral thread support forward? This is very important to us
and we are happy to get involved with the process and contribute if that
would be helpful.


Thanks for your consideration,

Alex Miller (Clojure team)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20260108/58e7bea8/attachment.htm>


More information about the loom-dev mailing list