JEP 454: Foreign Function and Memory API
openjdk.org
openjdk.org
This JEP will be in preview in Java 21 and the plan is to finalise it by Java 22. So it will be another ~2 years until it is part of an LTS release.
[1] https://dev.java/evolution/
- Bounds checks, we know about those. The JITC can move them around to reduce their costs.
- Temporal safety is new. It means you can't have crashes or read bad data due to use-after-free bugs, even though deallocation/unmapping is explicit and not GC controlled. It's efficient (see below).
- Thread safety. You can allocate memory that can't be shared between threads (confined segments).
- You can create customized memory allocators (arenas)
- You can allocate memory and give it to another piece of code, without that code being able to free it. This lets you communicate memory lifetimes via the type system.
- You can wrap memory allocated by external libraries to give these allocations these same abilities.
This isn't a set of compile-time guarantees like what Rust gives you, it's still Java, but it does provide the usual guarantees Java developers expect, like buggy code can't crash the VM.
It's also cheaper than you'd expect at runtime. The JIT compiler understands how to eliminate redundant bounds/free checks, so code that looks like it'd do a lot of bounds/free checking will only really do it once.
You could wonder how that works in a multithreaded language. What stops you allocating some memory, starting to work with it, the thread checks the memory isn't closed once and then does a lot of operations, but whilst it's doing that, another thread closes it? There's a TOCTOU problem. Their solution is to use the thread-local handshake mechanism built into the JVM, which lets threads send "messages" to each other even if the user's code isn't polling anything for a message. This is built on the safepoint mechanism, which is itself a highly optimized and efficient per-thread check. If a multi-threaded segment is closed then each thread that is accessing it is brought to a safepoint and then de-optimized (forced back to the interpreter from compiled code), at which point the flag will be checked on the next access and throw an exception.
In this way bounds and freed-flag checks can appear as if they are done on every single access, whilst still having high performance, and use-after-free bugs will always yield a safe exception that can be logged, reporting, doesn't corrupt memory etc.
I bet that games could very well be rewritten to use this to allocate memory. Though the layout and varhandles looks tedious to use, but may be it'd be fine.
Java deals pretty well with that case already. Small ephemeral objects have the allocation costs closer to C-style stack-allocation than heap allocation in most cases (due to TLAB etc.)
This has far bigger implications for databases, kv-stores, and similar, which are currently severely hamstrung by only being able to memory map 2 Gb at a time.
> I bet that games could very well be rewritten to use this to allocate memory. Though the layout and varhandles looks tedious to use, but may be it'd be fine.
I mean you could conceivably use it for an ECS system or something like that, scratch allocation, but many of those patterns from the C++ world solve problems that Java largely doesn't have (i.e. heap fragmentation)
It doesn't have anything to do with transforming arrays of structs into struct of arrays for better cache access.
https://en.wikipedia.org/wiki/Entity_component_system
Common ECS approaches are highly compatible with, and are often combined with, data-oriented design techniques. Data for all instances of a component are commonly stored together in physical memory, enabling efficient memory access for systems which operate over many entities.
so, I have some code with one loop, which reads strings from file and does some computations, when I add single "new SomeSmallObj()" there which I would allocate on stack in C, it starts working 2 times slower. How can I debug/reason/improve this case?..
var i = new SmallObject().getI();
Here's a good reading on it:https://gist.github.com/JohnTortugo/c2607821202634a6509ec3c3...
You can get an estimate of FFI overhead difference here: https://web.archive.org/web/20230715041452/https://vancan1ty...