panama and babylon
vectors, native code, and java on GPUs.
- chapter
- chapter 18.05
- reading time
- 9 min read
- preview
Project Panama connects java to native code and hardware. its foreign function and memory API has been final since java 22: call C functions and manage off-heap memory with no JNI glue. its vector API (SIMD) is still an incubator, twelve rounds in, deliberately waiting for valhalla's value classes.
Project Babylon is earlier: code reflection, letting a library read a java method as a model it can transform into something else, like a GPU kernel. as of october 2026 it lives in its own repository with no JEP.
the picture
you know how java has always been a little awkward at the edges: calling a C library meant writing JNI glue in C, and using the CPU's vector instructions meant hoping the JIT noticed? Panama is about those edges. Babylon is about a different edge: running java code somewhere other than a CPU (a GPU, a database, a machine learning runtime).
think of java as a country with very good roads inside its borders. Panama builds proper border crossings: to native libraries (FFM) and to the CPU's wide lanes (vectors). Babylon is about translation: taking a java method and re-expressing it in another country's language entirely.
foreign functions: done
this part shipped. here's C's strlen, called straight from java on jdk 25, with the string allocated off the heap in an arena that frees it the moment the block ends:
import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
void main() throws Throwable {
Linker linker = Linker.nativeLinker();
MethodHandle strlen = linker.downcallHandle( // C's size_t strlen(const char*)
linker.defaultLookup().find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
try (Arena arena = Arena.ofConfined()) {
MemorySegment s = arena.allocateFrom("hello, panama"); // a C string, off the heap
IO.println((long) strlen.invokeExact(s));
} // freed here, deterministically
}13
this one needs a real JVM (the foreign function api), so it doesn't run in the browser. copy it into java Main.java to try it.
it prints 13, and on jdk 25 also four WARNING: lines on stderr, starting with "A restricted method in java.lang.foreign.Linker has been called". that's JEP 472 (java 24) preparing to restrict native access: calling native code can crash the JVM, so you'll be expected to opt in with --enable-native-access=ALL-UNNAMED (or a module name). the full API is in the FFM API; the old way, and why it's being fenced in, is in JNI.
vectors: one instruction, many lanes
a CPU's SIMD instructions apply one operation to a whole register of numbers at once. the vector API lets you write that directly, portably, and have the JIT turn each vector operation into one instruction.
one instruction adds 4 floats at once. the Vector API asks for the widest shape the CPU has (SPECIES_PREFERRED) and the JIT turns each vector operation into one of these instructions.
b > c count, the rest are ignored, with no branch at all. the 128-bit row is what SPECIES_PREFERRED gave on the M4 mac this site measures on.so does it make code faster? i measured two loops over a million floats on jdk 25, scalar against the vector API (the API shape is in in preview):
// a[i] = b[i] * c[i] + a[i]
static void fmaScalar(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) a[i] = b[i] * c[i] + a[i];
}
// the sum of b[i] wherever b[i] > c[i], with a mask instead of an if
static float condVector(float[] b, float[] c) {
var acc = FloatVector.zero(S);
int i = 0;
for (; i < S.loopBound(b.length); i += S.length()) {
var vb = FloatVector.fromArray(S, b, i);
var m = vb.compare(VectorOperators.GT, FloatVector.fromArray(S, c, i));
acc = acc.add(vb, m); // add only the lanes where b > c
}
float s = acc.reduceLanes(VectorOperators.ADD);
for (; i < b.length; i++) if (b[i] > c[i]) s += b[i]; // the leftover tail
return s;
}$ java --add-modules jdk.incubator.vector Vec.java fma-scalar # and so on, one case per runthat's the honest summary of the vector API. for simple loops over arrays, C2's auto-vectorizer already emits SIMD instructions, and writing vectors by hand buys nothing. the API pays off where the JIT won't go on its own: here, a float sum (the JIT won't reorder float additions, since that can change the result) with a condition inside (random data, so the scalar branch is mispredicted half the time). the vector version reorders on purpose and has no branches. its result can differ from the scalar sum in the last few bits, for exactly that reason.
babylon: code as data
Babylon starts from a question: what if a library could read the code of a java method, not just call it? not bytecode (too low-level), not source text (too unstructured), but a code model: a tree of operations, types and values that keeps the structure of the java you wrote. a library could then transform it: into SQL for a query, into its own derivative for machine learning, into a kernel for a GPU.
hover a stage.
// Babylon (the openjdk/babylon repository, module jdk.incubator.code). not in any jdk:
// illustrative only, and the names have changed more than once.
import jdk.incubator.code.Reflect;
@Reflect // "keep a code model for this method"
static float squaredDistance(float x, float y) {
return x * x + y * y;
}
// a library asks for the method's model (Op.ofMethod(...) in the current repository),
// walks or transforms it, and generates its own code from it.be honest with yourself about the status: as of october 2026 there's no Babylon JEP, the module only exists in Babylon's repository, and the API has been reworked repeatedly. the project page's recent material (articles in january and may 2026 on optimizing GPU programs and using tensor cores from java) shows it's very active. but it's research you can build and try, not a feature you can depend on.
incubating since jdk 16 (JEP 338). it stays an incubator until valhalla's value classes are available as a preview, then moves to preview using them. value objects are integrated into jdk 28's preview, but no vector API JEP is on the 28 list yet.
- panama's FFM API is final (java 22): call C, manage off-heap memory, no JNI glue.
- native access is being fenced in: warnings since java 24 unless you use
--enable-native-access. - the vector API is still an incubator (twelfth in jdk 27), waiting for valhalla's value classes.
- measured: no gain where C2 already vectorizes; 4x on a branchy float sum with masks.
- babylon's code reflection is research in its own repository. no JEP as of october 2026.