首页 · ← SuperJ 手册

系统语言 · AOT · LLVM · 自托管

superJ

读起来像 Java
跑起来像 C//

Java 家族中的单线程、提前编译语言 — 不是它的子集。没有 VM,没有 JIT,没有垃圾回收器。熟悉的类、泛型与异常;底层是原生速度与 arena 作用域内存。

0 个垃圾回收器 0 个运行时线程 编译成 原生 代码 编译器用 SuperJ 写成
event.sj
// 看起来像 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);
  }
}

核心论点

保留让 Java 舒适的部分。丢掉妨碍原生性能的部分。

SuperJ 编译到 LLVM IR,再编译到机器码。没有字节码,没有类加载,没有追踪式回收器,也没有线程 — 整个程序在编译期就完全已知,因此行为可预测到每一次分配。

内存

Arena,而非 GC

一个全局 arena 供程序使用,外加词法 arena {} 块,在退出时以一次 O(1) 释放整体回收 — 没有 stop-the-world 停顿。

后端

通过 LLVM 生成原生代码

直接产出优化后的机器码。没有 VM,没有 JIT 预热,没有需要附带发布的运行时。

模型

单线程

线程、锁、管程与内存模型都被移除 — 由构造保证确定性。

自举

自托管

SuperJ 编译器用 SuperJ 写成 — 工具链端到端地构建自身。我们用自己的语言做内部验证。

标志性特性

确定性内存 — 而且编译器能证明它是安全的

arena 块内分配短生命周期对象;块退出时,整个 arena 一次性释放。一个会逃逸出其 arena 的引用是一个 编译错误,而不是潜伏的 use-after-free。

arena.sj
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 块。

arena temp { … } 分配中…
temp
Buffer 1 KB
byte[] payload
Frame ×3
✓  整个 arena 在一次 O(1) 释放中回收 — 无扫描、无停顿

↳  n(全局 arena)在块退出后仍然存活。

系统级能力

Java 触不可及的底层控制力

除了 arena 模型,SuperJ 还加入了那些你本来要退回 C 才能拿到的原语 — 而类型系统仍然在为你把关。

内存

Arena 与栈作用域

arena {} 用于 O(1) 批量回收;local 用于栈作用域引用。

数值

宽整数

一等公民 i128i256i512 原语,用于加密与大整数运算。

布局

固定布局结构体

无头的值类型,具有已知的线上布局 — 非常适合协议与 mmap。

数组

矩形数组

int[,] — 连续、按步长索引,与锯齿 int[][] 是不同的类型。

互操作

原生 C / Rust

native 方法直接调用 C ABI — 没有 JNI,没有 marshalling 层。

函数

方法引用

Class::method 指向单抽象方法(SAM)接口 — 无捕获,没有闭包。

泛型

擦除式泛型

类级与方法级、有界参数,以及 ? extends / ? super 通配符。

数据并行

SIMD 向量

可移植的向量族(Byte64Float8、…)下放为原生 SIMD IR。

安全

明确赋值

局部变量在赋值前读取是编译错误 — 不会意外读到垃圾值。

这笔交易

一份深思熟虑的清单

每一次移除都换来可预测性;每一项保留都是最初让 Java 高效的东西。

✗ 从 Java 中移除

  • // 线程、synchronizedvolatile
  • // 追踪式垃圾回收器
  • // 反射与类加载
  • // lambda 与闭包
  • // 自动装箱 / 拆箱
  • // 非静态内部类
  • // 受检异常、模块、finalize()

✓ 从 Java 中保留

  • // 类、接口、继承
  • // 带擦除 + 通配符的泛型
  • // 枚举与匿名类
  • // 异常(全部为非受检)
  • // 可变参数与方法引用
  • // 熟悉的控制流与语法
  • // String / string,单一不可变类型

证明

原生速度,平坦尾部

一个基于内置 HTTP 栈的 Web 服务器,编译成单个原生二进制,通过回环做压测 — 服务器固定在一个核上,负载生成器固定在另一个核上。它的服务速度比 wrk 发请求的速度还快。

515K
请求 / 秒 — wrk 上限约 423K,所以瓶颈是它
~1.1×
p99 在 p50 的 ~1.1× 以内 — 平坦尾部
0
GC 停顿 — arena 内存,无扫描

在 Ryzen 9 9950X3D 上测量,双方各占一个核(服务器在 CPU 4,客户端在 CPU 3):SuperJ 自带的流水线客户端达到 ~515K req/s,而 wrk 最高约 423K — 瓶颈是负载生成器,不是服务器。在持续负载下,服务器的尾部保持平坦 — p50 156µs,p99 167µs — 因为没有回收器会触发停顿。在别处,SuperJ 中一个缓存分块的 tiled GEMM 在各矩阵规模上都胜过 C 基线。

一句话,整个想法

读起来像 Java。跑起来像 C。
所以是 superJ。
superJ — ahead-of-time · LLVM · arena memory · self-hosted Powered by superJ — this site is served by superj_web 一门读起来像 Java、跑起来像 C 的语言 中文|EN