系统语言 · AOT · LLVM · 自托管
Java 家族中的单线程、提前编译语言 — 不是它的子集。没有 VM,没有 JIT,没有垃圾回收器。熟悉的类、泛型与异常;底层是原生速度与 arena 作用域内存。
// 看起来像 Java — 运行时没有 GC。
public class Report {
public static void main(String[] args) {
int total = 0;
arena scratch { // 作用域内存
int[] rows = new int[1<<16];
for (int i = 0; i < rows.length; i++)
total += (rows[i] = i * 3);
} // 在这里释放 — O(1),无停顿
System.out.println(total);
}
}
核心论点
SuperJ 编译到 LLVM IR,再编译到机器码。没有字节码,没有类加载,没有追踪式回收器,也没有线程 — 整个程序在编译期就完全已知,因此行为可预测到每一次分配。
一个全局 arena 供程序使用,外加词法 arena {} 块,在退出时以一次 O(1) 释放整体回收 — 没有 stop-the-world 停顿。
直接产出优化后的机器码。没有 VM,没有 JIT 预热,没有需要附带发布的运行时。
线程、锁、管程与内存模型都被移除 — 由构造保证确定性。
SuperJ 编译器用 SuperJ 写成 — 工具链端到端地构建自身。我们用自己的语言做内部验证。
标志性特性
在 arena 块内分配短生命周期对象;块退出时,整个 arena 一次性释放。一个会逃逸出其 arena 的引用是一个 编译错误,而不是潜伏的 use-after-free。
Node createNode(int v) {
return new Node(v); // 全局 arena — 可以安全返回
}
void process() {
arena temp {
Buffer buf = new Buffer(1024); // 在 temp 中
Node n = createNode(5); // 在全局中
// outer = buf; ← 被拒绝:逃逸出其 arena
} // temp 释放:buf 被回收,n 仍然有效
}
词法作用域,而非动态作用域。 一个 new 归属于它被写在其内的那个 arena — 被调用的方法仍然使用全局 arena,除非它自己开了一个 arena 块。
↳ n(全局 arena)在块退出后仍然存活。
系统级能力
除了 arena 模型,SuperJ 还加入了那些你本来要退回 C 才能拿到的原语 — 而类型系统仍然在为你把关。
arena {} 用于 O(1) 批量回收;local 用于栈作用域引用。
一等公民 i128、i256、i512 原语,用于加密与大整数运算。
无头的值类型,具有已知的线上布局 — 非常适合协议与 mmap。
int[,] — 连续、按步长索引,与锯齿 int[][] 是不同的类型。
native 方法直接调用 C ABI — 没有 JNI,没有 marshalling 层。
Class::method 指向单抽象方法(SAM)接口 — 无捕获,没有闭包。
类级与方法级、有界参数,以及 ? extends / ? super 通配符。
可移植的向量族(Byte64、Float8、…)下放为原生 SIMD IR。
局部变量在赋值前读取是编译错误 — 不会意外读到垃圾值。
这笔交易
每一次移除都换来可预测性;每一项保留都是最初让 Java 高效的东西。
synchronized、volatilefinalize()String / string,单一不可变类型证明
一个基于内置 HTTP 栈的 Web 服务器,编译成单个原生二进制,通过回环做压测 — 服务器固定在一个核上,负载生成器固定在另一个核上。它的服务速度比 wrk 发请求的速度还快。
在 Ryzen 9 9950X3D 上测量,双方各占一个核(服务器在 CPU 4,客户端在 CPU 3):SuperJ 自带的流水线客户端达到 ~515K req/s,而 wrk 最高约 423K — 瓶颈是负载生成器,不是服务器。在持续负载下,服务器的尾部保持平坦 — p50 156µs,p99 167µs — 因为没有回收器会触发停顿。在别处,SuperJ 中一个缓存分块的 tiled GEMM 在各矩阵规模上都胜过 C 基线。
一句话,整个想法