[External] : Re: jstack, profilers and other tools

Ron Pressler ron.pressler at oracle.com
Sat Jul 23 01:00:50 UTC 2022



> On 23 Jul 2022, at 00:06, Alex Otenko <oleksandr.otenko at gmail.com> wrote:
> 
> I am familiar with the bijection between types and continuation-passing. There is however a barrier. Who does the thing of interest in each of the styles: the caller or the callee.
> 
> In terms of type theory it doesn't matter; both styles have the same power. In terms of software engineering it does matter. It is impossible to plug custom logic in a library that you import, unless a mechanism is planned at design time, and built in - most commonly a bunch of configuration options to tweak a parameterised algorithm; not really the ability to implement arbitrary algorithms that the theoretical bijection requires. So in practice you are limited to doing only things a caller can do.
> 
> Same for allocation. It is doable to get the same behaviour as in async API, but it is not what InputStream.read(byte[]) does - the method signature forces you to allocate and wait. And there is no mechanism to tell the blocking API to read 10k of 100 requests instead of 100 bytes of 10k requests. It's doable, but it is not there.

No, all that is simply incorrect. The customisability of synchronous code is at least as ergonomic and flexible as for async code if not more so (because Java is built around synchronous primitives, and its basic composition operators are made for synchronous primitives), and the I/O buffering primitives provided by the JDK are no different between synchronous and asynchronous.

I feel like you’re trying to find some justification for your aesthetic preference for asynchronous code, and there’s really no need. If you enjoy it more, and you don’t need the observability support from the runtime — by all means keep using it. Our goal isn’t to get fans of asynchronous code to abandon using it. It is to give the same benefits to those who prefer using synchronous code, and we can even go further because it is a better fit for the design of the Java platform and so that’s where we can support the code better, both in the runtime and the language. But if you like async better — use async.

But while we’re in the weeds, there are some interesting differences re memory usage, although they are not fundamentally about sync vs async, but some technical specifics of the JDK and how virtual threads are implemented. This might be of interest to the curious.

Whether you use the async or sync style, there’s need to pass data from computation done before the call to computation done after. In Java, the async style normally requires allocating a new object to do that, while sync reuses the same mutable stack. Hypothetically, async code could implement such a mutable stack manually, but in Java it is difficult, because, unlike in C, heap objects cannot switch between storing pointers and primitives in the same memory cell. Stacks are designed to allow that thanks to special GC protocols, that were adapted for virtual threads.

On the other hand, for simplicity and performance reasons in the JIT compiler, the way data is stored in the stack is wasteful (so fewer but bigger objects are allocated). We’re now working on making it more compact in the case of virtual threads.

— Ron


More information about the loom-dev mailing list