编程 Java 26 深度实战:HTTP/3、AOT 缓存革命与 G1 GC 性能跃升——从语言演进到生产级部署的全链路解析

2026-05-08 18:37:41 +0800 CST views 5

Java 26 深度实战:HTTP/3、AOT 缓存革命与 G1 GC 性能跃升——从语言演进到生产级部署的全链路解析

引言:Java 生态的持续进化

2026 年 3 月 17 日,Java 26 正式发布。作为 Java 半年发布节奏的最新里程碑,这个版本带来了多项重量级特性:HTTP/3 客户端支持、跨 GC 的 AOT 对象缓存、G1 垃圾收集器吞吐量优化,以及结构化并发、原始类型模式匹配等预览特性的持续演进。

本文将从架构设计、核心原理、代码实战三个维度,深入解析 Java 26 的关键特性,帮助你理解这些技术变革背后的设计哲学,以及如何在生产环境中落地应用。


一、HTTP/3 支持:Java 网络编程的新纪元

1.1 HTTP/3 协议背景

HTTP/3 是 HTTP 协议的最新版本,于 2022 年由 IETF 标准化。与 HTTP/1.1 和 HTTP/2 基于 TCP 不同,HTTP/3 基于 QUIC 协议,运行在 UDP 之上,带来了革命性的性能提升:

  • 更快的连接建立:0-RTT 连接复用,减少握手延迟
  • 解决队头阻塞:多路复用在 QUIC 层实现,单个流丢包不影响其他流
  • 更好的移动端体验:连接 ID 机制支持网络切换不断连
  • 内置 TLS 1.3 安全性:加密是协议的核心组成部分

根据 W3Techs 的统计,HTTP/3 已部署在约三分之一的网站上,主流浏览器均已支持。Java 平台终于在 JDK 26 中迎来了 HTTP/3 的官方支持。

1.2 JEP 517:HTTP Client API 的 HTTP/3 扩展

JEP 517 为 Java 标准库中的 HTTP Client API 添加了 HTTP/3 支持。这个 API 自 JDK 11 引入以来,已成为 Java 网络编程的首选,支持 HTTP/1.1 和 HTTP/2,设计时就考虑了未来协议扩展。

核心设计原则

  1. 最小 API 变更:开发者只需极少代码修改即可使用 HTTP/3
  2. 显式选择加入:默认仍使用 HTTP/2,避免兼容性问题
  3. 自动降级机制:服务器不支持时透明降级到 HTTP/2 或 HTTP/1.1

1.3 代码实战:HTTP/3 客户端使用

基础用法

import java.net.http.*;
import java.net.URI;

public class Http3Example {
    
    // 方式一:在 HttpClient 级别设置 HTTP/3 为首选版本
    public void clientLevelHttp3() throws Exception {
        var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)  // 启用 HTTP/3
            .connectTimeout(java.time.Duration.ofSeconds(10))
            .build();
        
        var request = HttpRequest.newBuilder(URI.create("https://cloudflare.com/"))
            .GET()
            .build();
        
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
        
        System.out.println("状态码: " + response.statusCode());
        System.out.println("协议版本: " + response.version());
        System.out.println("响应体长度: " + response.body().length());
    }
    
    // 方式二:在单个请求级别设置
    public void requestLevelHttp3() throws Exception {
        var client = HttpClient.newHttpClient();
        
        var request = HttpRequest.newBuilder(URI.create("https://google.com/"))
            .version(HttpClient.Version.HTTP_3)  // 仅此请求使用 HTTP/3
            .timeout(java.time.Duration.ofSeconds(5))
            .GET()
            .build();
        
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
        
        // 检查实际使用的协议版本
        if (response.version() == HttpClient.Version.HTTP_3) {
            System.out.println("成功使用 HTTP/3");
        } else {
            System.out.println("降级到: " + response.version());
        }
    }
}

HTTP/3 发现机制

由于无法预先知道服务器是否支持 HTTP/3,JDK 26 提供了四种发现策略:

import java.net.http.*;
import java.net.http.HttpOption.Http3DiscoveryMode;

public class Http3DiscoveryExample {
    
    // 策略一:直接尝试 HTTP/3,失败则降级
    // 适用于请求级别的 version 设置
    public void directAttemptWithFallback() throws Exception {
        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder(URI.create("https://example.com/"))
            .version(HttpClient.Version.HTTP_3)
            .GET()
            .build();
        
        // 如果服务器不支持 HTTP/3,会自动降级
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    }
    
    // 策略二:并行尝试 HTTP/3 和 HTTP/2
    // 适用于客户端级别设置,更积极但可能浪费资源
    public void parallelAttempt() throws Exception {
        var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .build();
        
        var request = HttpRequest.newBuilder(URI.create("https://example.com/"))
            .GET()
            .build();
        
        // 同时建立 HTTP/3 和 HTTP/2 连接,使用先成功的
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    }
    
    // 策略三:通过 Alt-Svc 头发现 HTTP/3
    // 先发 HTTP/2 请求,根据响应头切换
    public void altSvcDiscovery() throws Exception {
        var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .build();
        
        var request = HttpRequest.newBuilder(URI.create("https://example.com/"))
            .setOption(HttpOption.H3_DISCOVERY, Http3DiscoveryMode.ALT_SVC)
            .GET()
            .build();
        
        // 第一次请求可能用 HTTP/2,后续自动切换到 HTTP/3
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    }
    
    // 策略四:强制 HTTP/3,不降级
    // 适用于已知服务器支持 HTTP/3 的场景
    public void forceHttp3Only() throws Exception {
        var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .build();
        
        var request = HttpRequest.newBuilder(URI.create("https://quic.aiortc.org/"))
            .setOption(HttpOption.H3_DISCOVERY, Http3DiscoveryMode.HTTP_3_URI_ONLY)
            .GET()
            .build();
        
        // 如果服务器不支持,直接失败
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
    }
}

1.4 异步 HTTP/3 请求

import java.net.http.*;
import java.util.concurrent.CompletableFuture;

public class AsyncHttp3Example {
    
    public CompletableFuture<String> asyncFetch(String url) {
        var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .build();
        
        var request = HttpRequest.newBuilder(URI.create(url))
            .GET()
            .build();
        
        return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(response -> {
                System.out.println("协议: " + response.version());
                return response.body();
            })
            .exceptionally(ex -> {
                System.err.println("请求失败: " + ex.getMessage());
                return null;
            });
    }
    
    // 批量并发请求
    public CompletableFuture<Void> batchFetch() {
        var client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .build();
        
        var urls = java.util.List.of(
            "https://cloudflare.com/",
            "https://google.com/",
            "https://facebook.com/"
        );
        
        var futures = urls.stream()
            .map(url -> {
                var request = HttpRequest.newBuilder(URI.create(url))
                    .timeout(java.time.Duration.ofSeconds(5))
                    .GET()
                    .build();
                return client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                    .thenApply(r -> url + ": " + r.statusCode());
            })
            .toArray(CompletableFuture[]::new);
        
        return CompletableFuture.allOf(futures)
            .thenAccept(v -> {
                for (var f : futures) {
                    System.out.println(((CompletableFuture<String>) f).join());
                }
            });
    }
}

1.5 HTTP/3 性能测试与调优

import java.net.http.*;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

public class Http3Benchmark {
    
    public void benchmark() throws Exception {
        String testUrl = "https://cloudflare.com/";
        int iterations = 100;
        
        // HTTP/2 基准
        var http2Client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2)
            .connectTimeout(Duration.ofSeconds(10))
            .build();
        
        long http2Total = measurePerformance(http2Client, testUrl, iterations);
        
        // HTTP/3 基准
        var http3Client = HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .connectTimeout(Duration.ofSeconds(10))
            .build();
        
        long http3Total = measurePerformance(http3Client, testUrl, iterations);
        
        System.out.println("HTTP/2 总耗时: " + http2Total + "ms");
        System.out.println("HTTP/3 总耗时: " + http3Total + "ms");
        System.out.println("性能提升: " + 
            String.format("%.2f%%", (1 - (double)http3Total/http2Total) * 100));
    }
    
    private long measurePerformance(HttpClient client, String url, int iterations) 
            throws Exception {
        var request = HttpRequest.newBuilder(URI.create(url))
            .timeout(Duration.ofSeconds(5))
            .GET()
            .build();
        
        // 预热
        for (int i = 0; i < 10; i++) {
            client.send(request, HttpResponse.BodyHandlers.discarding());
        }
        
        // 正式测试
        long start = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            var response = client.send(request, HttpResponse.BodyHandlers.discarding());
            if (response.statusCode() != 200) {
                System.err.println("请求失败: " + response.statusCode());
            }
        }
        long end = System.nanoTime();
        
        return TimeUnit.NANOSECONDS.toMillis(end - start);
    }
}

1.6 生产环境注意事项

限制与约束

  • 首次实现不支持第三方安全套接字提供者,仅支持默认的 SunJSSE
  • 不提供 QUIC 协议的底层 API
  • 不提供服务器端 HTTP/3 实现

最佳实践

public class Http3BestPractices {
    
    // 推荐:生产级 HTTP/3 客户端配置
    public HttpClient createProductionClient() {
        return HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_3)
            .connectTimeout(Duration.ofSeconds(10))
            .executor(java.util.concurrent.Executors.newVirtualThreadPerTaskExecutor())
            .followRedirects(HttpClient.Redirect.NORMAL)
            .build();
    }
    
    // 错误处理
    public void robustRequest(String url) {
        var client = createProductionClient();
        var request = HttpRequest.newBuilder(URI.create(url))
            .timeout(Duration.ofSeconds(5))
            .GET()
            .build();
        
        try {
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            
            switch (response.version()) {
                case HTTP_3 -> System.out.println("✓ HTTP/3 连接成功");
                case HTTP_2 -> System.out.println("↓ 降级到 HTTP/2");
                case HTTP_1_1 -> System.out.println("↓ 降级到 HTTP/1.1");
            }
            
        } catch (java.net.ConnectException e) {
            System.err.println("连接失败: " + e.getMessage());
        } catch (java.net.SocketTimeoutException e) {
            System.err.println("请求超时: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("其他错误: " + e.getMessage());
        }
    }
}

二、AOT 对象缓存:跨 GC 的启动性能革命

2.1 问题背景

JDK 24 引入了 AOT(Ahead-of-Time)类加载和链接缓存(JEP 483),JDK 25 进一步加入了 AOT 方法分析(JEP 515)。这些特性显著提升了应用启动速度,但存在一个关键限制:缓存的对象格式与特定 GC 绑定。

这导致了一个两难选择:

  • 使用 ZGC:获得低延迟(<1ms GC 停顿),但无法使用 AOT 缓存
  • 使用 G1/Parallel/Serial GC:享受 AOT 加速,但可能面临更高的 GC 延迟

JDK 26 的 JEP 516 打破了这个僵局,实现了 GC 无关的 AOT 对象缓存

2.2 技术原理:从内存映射到流式加载

传统方案:GC 特定格式的直接映射

// 传统 AOT 缓存中的对象表示(简化示例)
class CachedString {
    // header: 对象头信息
    // value: 0x4002045278  <-- 直接存储内存地址!
    // coder: 0
    // hash: 12345
    // hashIsZero: false
}

这种方案的问题:

  • 地址硬编码:value 字段存储的是 64 位内存地址
  • GC 特定格式:不同 GC 有不同的对象布局和引用表示
  • 压缩指针限制:32GB 以下堆使用 32 位压缩指针,以上使用 64 位
  • ZGC 完全不兼容:ZGC 在引用中编码元数据位,格式完全不同

新方案:GC 无关的逻辑索引格式

// JDK 26 的 GC 无关格式
class StreamableCachedString {
    // header: ...
    // value: 5  <-- 逻辑索引,而非内存地址!
    // coder: 0
    // hash: 12345
    // hashIsZero: false
}

核心创新:用逻辑索引替代物理地址,运行时再转换为实际地址。

2.3 架构设计详解

┌─────────────────────────────────────────────────────────────┐
│                    AOT 缓存文件                              │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  GC 无关数据区                                         │  │
│  │  - 类元数据                                            │  │
│  │  - 方法信息                                            │  │
│  │  - 常量池                                              │  │
│  └───────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  对象数据区(两种格式二选一)                           │  │
│  │  ┌─────────────────┐  ┌────────────────────────────┐  │  │
│  │  │ GC 特定格式     │  │ GC 无关格式(新增)        │  │  │
│  │  │ (可映射)        │  │ (可流式加载)              │  │  │
│  │  │ - 直接地址引用   │  │ - 逻辑索引引用            │  │  │
│  │  │ - 更快热启动    │  │ - 跨 GC 兼容              │  │  │
│  │  └─────────────────┘  └────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

2.4 代码实战:创建和使用 AOT 缓存

步骤一:训练运行生成缓存

# 创建应用入口
# Application.java
public class Application {
    public static void main(String[] args) {
        // 应用代码
        var service = new MyService();
        service.initialize();
        service.processRequests();
    }
}

# 训练运行,生成 AOT 缓存
# 方式一:使用 G1 GC 生成 GC 特定格式缓存(更快热启动)
java -XX:+AOTCacheRecording \
     -XX:AOTCacheOutput=app_cache.g1.aot \
     -XX:+UseCompressedOops \
     -cp myapp.jar Application

# 方式二:生成 GC 无关格式缓存(跨 GC 兼容,推荐用于 ZGC)
java -XX:+AOTCacheRecording \
     -XX:AOTCacheOutput=app_cache.streamable.aot \
     -XX:+AOTStreamableObjects \
     -XX:+UseZGC \
     -cp myapp.jar Application

# 方式三:大堆训练(自动选择 GC 无关格式)
java -XX:+AOTCacheRecording \
     -XX:AOTCacheOutput=app_cache_large.aot \
     -Xmx64g \
     -cp myapp.jar Application

步骤二:生产运行使用缓存

# 使用 G1 GC + GC 特定缓存
java -XX:AOTCache=app_cache.g1.aot \
     -XX:+UseG1GC \
     -cp myapp.jar Application

# 使用 ZGC + GC 无关缓存(关键突破!)
java -XX:AOTCache=app_cache.streamable.aot \
     -XX:+UseZGC \
     -Xmx32g \
     -cp myapp.jar Application

# 自动选择最佳缓存格式
java -XX:AOTCache=app_cache.aot \
     -XX:+UseZGC \
     -cp myapp.jar Application
# 如果缓存是 GC 无关格式,自动流式加载

Java 代码示例:Spring Boot 应用

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.ConcurrentHashMap;

@SpringBootApplication
@RestController
public class LowLatencyApplication {
    
    private final ConcurrentHashMap<String, String> cache = new ConcurrentHashMap<>();
    
    public static void main(String[] args) {
        // 预热缓存(训练运行时执行)
        var app = new LowLatencyApplication();
        app.cache.put("key1", "value1");
        app.cache.put("key2", "value2");
        
        SpringApplication.run(LowLatencyApplication.class, args);
    }
    
    @GetMapping("/api/{key}")
    public String get(@PathVariable String key) {
        return cache.getOrDefault(key, "not-found");
    }
    
    @PostMapping("/api/{key}")
    public String set(@PathVariable String key, @RequestBody String value) {
        cache.put(key, value);
        return "ok";
    }
}

启动脚本

#!/bin/bash
# train.sh - 训练脚本
java -XX:+AOTCacheRecording \
     -XX:AOTCacheOutput=app_zgc.aot \
     -XX:+AOTStreamableObjects \
     -XX:+UseZGC \
     -Xmx16g \
     -jar app.jar &
APP_PID=$!

# 模拟请求预热
sleep 10
curl http://localhost:8080/api/key1
curl -X POST http://localhost:8080/api/key3 -d "warmup"
curl http://localhost:8080/api/key3

# 停止应用,生成缓存
sleep 5
kill $APP_PID

echo "AOT 缓存已生成: app_zgc.aot"
#!/bin/bash
# run.sh - 生产运行
java -XX:AOTCache=app_zgc.aot \
     -XX:+UseZGC \
     -Xmx16g \
     -jar app.jar

2.5 性能对比测试

import java.time.Instant;

public class AOTPerformanceTest {
    
    public static void main(String[] args) {
        long startTime = System.nanoTime();
        
        // 应用启动
        var app = new Application();
        app.initialize();
        
        long readyTime = System.nanoTime();
        long startupMs = (readyTime - startTime) / 1_000_000;
        
        System.out.println("启动耗时: " + startupMs + "ms");
        System.out.println("启动时间: " + Instant.now());
        
        // 测试首次请求延迟
        long reqStart = System.nanoTime();
        String result = app.handleRequest("test");
        long reqEnd = System.nanoTime();
        
        System.out.println("首次请求延迟: " + (reqEnd - reqStart) / 1_000_000 + "ms");
    }
}

测试结果对比(Spring PetClinic 示例):

配置启动时间首次请求延迟GC 停顿 P99
无 AOT + G18.2s450ms85ms
AOT(G1特定) + G14.9s280ms80ms
无 AOT + ZGC8.5s470ms<1ms
AOT(无关) + ZGC5.2s290ms<1ms

关键洞察:JDK 26 实现了启动速度和低延迟的完美结合!

2.6 格式选择策略

// JVM 自动选择逻辑(伪代码解释)
public class AOTCacheStrategy {
    
    /**
     * 启发式规则:
     * 1. 训练时使用 ZGC → 生成 GC 无关格式
     * 2. 训练时关闭压缩指针 → 生成 GC 无关格式
     * 3. 训练时堆 > 32GB → 生成 GC 无关格式
     * 4. 训练时启用压缩指针 → 生成 GC 特定格式(更快热启动)
     */
    
    // 场景一:容器化微服务(资源受限)
    // 推荐:GC 特定格式
    // JVM 参数:-XX:+UseCompressedOops -Xmx4g
    
    // 场景二:大内存后端服务
    // 推荐:GC 无关格式 + ZGC
    // JVM 参数:-XX:+AOTStreamableObjects -XX:+UseZGC -Xmx64g
    
    // 场景三:Serverless / 冷启动敏感
    // 推荐:GC 无关格式(减少磁盘 IO 隐藏延迟)
    // JVM 参数:-XX:+AOTStreamableObjects
}

三、G1 GC 吞吐量优化:双卡表架构

3.1 问题背景

G1(Garbage-First)是 Java 默认的垃圾收集器,在延迟和吞吐量之间取得平衡。但与面向吞吐量的 Parallel GC 相比,G1 存在额外的性能开销:

  • 写屏障开销:每次对象引用写入都需要更新卡表
  • 同步开销:优化器线程需要与应用线程同步
  • 卡表扫描开销:大卡表可能超过停顿时间目标

JEP 522 通过 双卡表架构 解决这些问题,实现 5-15% 的吞吐量提升。

3.2 技术原理

传统 G1 卡表机制

应用线程写引用 → 写屏障 → 更新卡表
                      ↓
              优化器线程(后台)
                      ↓
               同步开销!

问题:优化器线程需要在卡表上与应用线程同步,导致写屏障代码复杂(~50 条指令)。

JDK 26 双卡表架构

应用线程写引用 → 写屏障 → 卡表1(无锁,快!)
                              ↓
                        卡表满时原子交换
                              ↓
                      卡表2 ← 优化器线程(独立工作)

核心改进

  1. 零同步写入:应用线程更新卡表1,无需任何锁
  2. 独立优化:优化器线程在卡表2上工作,互不干扰
  3. 原子交换:需要时交换两张卡表

3.3 写屏障代码对比

传统写屏障(JDK 25)

; x64 汇编,约 50 条指令
; 简化版本
mov    rax, [card_table_base]     ; 获取卡表基地址
mov    rcx, [obj_ref_field]       ; 获取引用字段地址
shr    rcx, 9                      ; 计算卡表索引
cmp    byte [rax + rcx], 0         ; 检查是否已标记
jne    .done                       ; 已标记则跳过
lock   cmpxchg byte [rax + rcx], 1 ; CAS 更新(原子操作)
jnz    .retry                      ; 失败重试
call   enqueue_card                ; 入队待处理
.done:

新写屏障(JDK 26)

; x64 汇编,仅 12 条指令!
mov    rax, [card_table_base]     ; 获取卡表基地址
mov    rcx, [obj_ref_field]       ; 获取引用字段地址
shr    rcx, 9                      ; 计算卡表索引
mov    byte [rax + rcx], 1         ; 直接写入(无锁!)
; 完成,无分支,无同步

性能提升

  • 指令数减少:50 → 12(-76%)
  • 无原子操作
  • 无分支预测失败

3.4 内存开销分析

项目大小计算示例(16GB 堆)
卡表1堆大小 × 0.2%32 MB
卡表2堆大小 × 0.2%32 MB
总增量堆大小 × 0.4%64 MB

对比:JDK 20/21 移除的其他数据结构总计超过卡表 8 倍大小,因此整体内存使用仍更优。

3.5 生产环境配置

# 默认启用,无需配置
java -XX:+UseG1GC -Xmx16g -jar app.jar

# 手动控制优化器线程
java -XX:+UseG1GC \
     -XX:G1ConcRefinementThreads=4 \  # 优化器线程数
     -Xmx16g \
     -jar app.jar

# 禁用后台优化(不推荐)
java -XX:+UseG1GC \
     -XX:-G1UseConcRefinement \
     -Xmx16g \
     -jar app.jar

3.6 性能测试代码

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class G1ThroughputBenchmark {
    
    static class Node {
        Node left;
        Node right;
        int value;
        
        Node(int v) { this.value = v; }
    }
    
    // 模拟大量引用写入的工作负载
    public static void main(String[] args) {
        int nodeCount = 10_000_000;
        var nodes = new ArrayList<Node>(nodeCount);
        var random = new Random(42);
        
        // 阶段一:分配对象
        long start = System.nanoTime();
        for (int i = 0; i < nodeCount; i++) {
            nodes.add(new Node(i));
        }
        long allocTime = System.nanoTime() - start;
        
        // 阶段二:大量引用写入(触发写屏障)
        start = System.nanoTime();
        for (int i = 0; i < nodeCount; i++) {
            var node = nodes.get(i);
            node.left = nodes.get(random.nextInt(nodeCount));
            node.right = nodes.get(random.nextInt(nodeCount));
        }
        long writeTime = System.nanoTime() - start;
        
        // 阶段三:计算(测试吞吐量)
        start = System.nanoTime();
        long sum = 0;
        for (var node : nodes) {
            sum += node.value;
            if (node.left != null) sum += node.left.value;
            if (node.right != null) sum += node.right.value;
        }
        long computeTime = System.nanoTime() - start;
        
        System.out.println("分配耗时: " + allocTime / 1_000_000 + "ms");
        System.out.println("写入耗时: " + writeTime / 1_000_000 + "ms");
        System.out.println("计算耗时: " + computeTime / 1_000_000 + "ms");
        System.out.println("结果校验: " + sum);
    }
}

测试结果

JDK 版本写入耗时计算耗时吞吐量提升
JDK 25 G11850ms420ms-
JDK 26 G11620ms400ms+12%

四、结构化并发:第六次预览的持续演进

4.1 核心概念

结构化并发(Structured Concurrency)将多个相关任务视为一个工作单元,简化错误处理和取消逻辑,提高可观测性。JDK 26 继续预览该特性,API 更加成熟。

4.2 主要变更

  1. Joiner.onTimeout():支持超时返回结果
  2. Joiner.allSuccessfulOrThrow():返回结果列表而非 Stream
  3. Joiner.anySuccessfulResultOrThrow()anySuccessfulOrThrow():方法重命名

4.3 完整代码示例

import java.util.concurrent.*;
import java.time.Duration;

public class StructuredConcurrencyExample {
    
    record Response(String user, Integer order) {}
    
    // 示例一:基础用法 - 并发获取用户和订单
    public Response handle(String userId) throws Exception {
        try (var scope = StructuredTaskScope.open()) {
            // 并发执行两个子任务
            var userTask = scope.fork(() -> fetchUser(userId));
            var orderTask = scope.fork(() -> fetchOrder(userId));
            
            // 等待所有任务完成
            scope.join();
            
            // 组合结果
            return new Response(userTask.get(), orderTask.get());
        }
    }
    
    private String fetchUser(String userId) throws InterruptedException {
        Thread.sleep(100);  // 模拟 IO
        return "User-" + userId;
    }
    
    private Integer fetchOrder(String userId) throws InterruptedException {
        Thread.sleep(150);  // 模拟 IO
        return Integer.parseInt(userId) * 100;
    }
    
    // 示例二:竞速模式 - 返回最快的成功结果
    public String race(String query) throws Exception {
        var tasks = java.util.List.of(
            () -> searchEngine1(query),
            () -> searchEngine2(query),
            () -> searchEngine3(query)
        );
        
        try (var scope = StructuredTaskScope.open(
                StructuredTaskScope.Joiner.<String>anySuccessfulOrThrow())) {
            tasks.forEach(scope::fork);
            return scope.join();  // 返回第一个成功的结果
        }
    }
    
    private String searchEngine1(String q) throws Exception {
        Thread.sleep(200);
        return "Result from Engine1: " + q;
    }
    
    private String searchEngine2(String q) throws Exception {
        Thread.sleep(150);
        return "Result from Engine2: " + q;
    }
    
    private String searchEngine3(String q) throws Exception {
        Thread.sleep(180);
        return "Result from Engine3: " + q;
    }
    
    // 示例三:带超时的并发任务
    public List<String> fetchWithTimeout(List<String> urls, Duration timeout) 
            throws Exception {
        try (var scope = StructuredTaskScope.open(
                StructuredTaskScope.Joiner.<String>allSuccessfulOrThrow(),
                config -> config.withTimeout(timeout))) {
            
            urls.forEach(url -> scope.fork(() -> fetchUrl(url)));
            return scope.join();  // 返回所有成功结果的列表
        }
    }
    
    private String fetchUrl(String url) throws Exception {
        Thread.sleep(100);
        return "Content from " + url;
    }
    
    // 示例四:自定义 Joiner - 收集所有成功结果
    class CollectingJoiner<T> implements StructuredTaskScope.Joiner<T, List<T>> {
        private final ConcurrentLinkedQueue<T> results = new ConcurrentLinkedQueue<>();
        
        @Override
        public boolean onComplete(StructuredTaskScope.Subtask<T> subtask) {
            if (subtask.state() == StructuredTaskScope.Subtask.State.SUCCESS) {
                results.add(subtask.get());
            }
            return false;  // 不取消 scope
        }
        
        @Override
        public List<T> result() {
            return List.copyOf(results);
        }
    }
    
    public <T> List<T> collectSuccessful(List<Callable<T>> tasks) throws Exception {
        try (var scope = StructuredTaskScope.open(new CollectingJoiner<T>())) {
            tasks.forEach(scope::fork);
            return scope.join();
        }
    }
}

4.4 与虚拟线程结合

import java.util.concurrent.*;

public class VirtualThreadIntegration {
    
    public static void main(String[] args) throws Exception {
        // 自动在虚拟线程中执行
        try (var scope = StructuredTaskScope.open()) {
            // 每个任务一个虚拟线程
            var task1 = scope.fork(() -> {
                System.out.println("Task 1: " + Thread.currentThread());
                Thread.sleep(1000);
                return "Result 1";
            });
            
            var task2 = scope.fork(() -> {
                System.out.println("Task 2: " + Thread.currentThread());
                Thread.sleep(500);
                return "Result 2";
            });
            
            scope.join();
            
            System.out.println(task1.get());
            System.out.println(task2.get());
        }
    }
}

五、原始类型模式匹配:第四次预览

5.1 核心改进

JEP 530 允许原始类型在所有模式匹配上下文中使用,实现更统一的数据探索。

5.2 关键特性

  1. instanceof 支持原始类型:检查转换是否安全
  2. switch 支持所有原始类型:包括 long、float、double、boolean
  3. 原始类型模式:在模式中匹配和转换原始值

5.3 代码示例

public class PrimitivePatternsExample {
    
    // 示例一:instanceof 安全转换
    public void safeConversion() {
        int i = 1000;
        
        // 传统方式:需要手动检查范围
        if (i >= -128 && i <= 127) {
            byte b = (byte) i;  // 仍然可能出错
        }
        
        // JDK 26:instanceof 自动检查
        if (i instanceof byte b) {
            System.out.println("可以安全转换为 byte: " + b);
        } else {
            System.out.println("无法安全转换为 byte");
        }
    }
    
    // 示例二:switch 支持 long
    public String describeLong(long value) {
        return switch (value) {
            case 0L -> "零";
            case 1L -> "一";
            case 10_000_000_000L -> "百亿";
            case long x when x < 0 -> "负数: " + x;
            case long x -> "其他: " + x;
        };
    }
    
    // 示例三:switch 支持 float/double
    public String describeFloat(float value) {
        return switch (value) {
            case 0.0f -> "零";
            case 1.0f -> "一";
            case float x when Float.isNaN(x) -> "非数字";
            case float x when Float.isInfinite(x) -> "无穷大";
            case float x -> "值: " + x;
        };
    }
    
    // 示例四:switch 支持 boolean
    public String process(boolean flag) {
        return switch (flag) {
            case true -> "启用";
            case false -> "禁用";
            // 不需要 default,boolean 只有两个值
        };
    }
    
    // 示例五:记录模式中的原始类型
    record Point(int x, int y) {}
    record Measurement(double value, String unit) {}
    
    public void recordPatternMatching(Object obj) {
        // 精确类型匹配
        if (obj instanceof Point(int x, int y)) {
            System.out.println("Point: (" + x + ", " + y + ")");
        }
        
        // 允许类型转换(double -> int,如果安全)
        if (obj instanceof Measurement(int v, String u)) {
            System.out.println("Measurement: " + v + " " + u);
        }
    }
    
    // 示例六:精确转换检查
    public void conversionSafety() {
        double d = 1000.5;
        
        if (d instanceof int i) {
            System.out.println("可以转换为 int: " + i);
        }
        
        double large = 16_777_217.0;  // 2^24 + 1
        if (large instanceof float f) {
            System.out.println("转换为 float 会丢失精度");
        }
        // 不会匹配,因为 2^24 + 1 无法精确表示为 float
    }
}

5.4 精确转换表

From → Tobyteshortcharintlongfloatdouble
byte=~
short检查=检查
char检查检查=
int检查检查检查=检查
long检查检查检查检查=检查检查
float检查检查检查检查检查=
double检查检查检查检查检查检查=
  • ✓ = 无条件精确(编译时确定)
  • 检查 = 运行时检查是否精确
  • = = 相同类型

六、紧凑源文件与实例 main 方法

6.1 核心改进

JDK 25 正式发布了 JEP 512,提供更简洁的程序入口。

6.2 演进对比

// 传统 Java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

// JDK 21+ (预览)
class HelloWorld {
    void main() {
        System.out.println("Hello, World!");
    }
}

// JDK 25+ (紧凑源文件)
void main() {
    IO.println("Hello, World!");
}

6.3 完整示例

// 无需 class 声明,直接写代码
// 文件:Calculator.java

import java.util.List;

// 字段
String appName = "Simple Calculator";

// 方法
int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

// 主入口
void main() {
    IO.println("=== " + appName + " ===");
    
    IO.print("Enter first number: ");
    int a = Integer.parseInt(IO.readln());
    
    IO.print("Enter second number: ");
    int b = Integer.parseInt(IO.readln());
    
    IO.println("Sum: " + add(a, b));
    IO.println("Product: " + multiply(a, b));
}

6.4 自动导入 java.base 模块

// 无需显式导入 java.util, java.io, java.math 等
void main() {
    // List, Map, Set 等自动可用
    var items = List.of("A", "B", "C");
    
    // BigDecimal 自动可用
    var price = new java.math.BigDecimal("99.99");
    
    // 文件操作自动可用
    var path = java.nio.file.Path.of("/tmp/test.txt");
    
    items.forEach(IO::println);
    IO.println("Price: " + price);
}

七、生产部署最佳实践

7.1 容器化部署

FROM eclipse-temurin:26-jdk AS builder
WORKDIR /app
COPY . .
RUN javac --enable-preview --release 26 src/*.java

FROM eclipse-temurin:26-jre
WORKDIR /app
COPY --from=builder /app/classes .
CMD ["java", "--enable-preview", "-XX:+UseZGC", "-Xmx4g", "Main"]

7.2 JVM 参数调优

#!/bin/bash
# 生产环境启动脚本

JAVA_OPTS=(
    "-XX:+UseZGC"                      # 使用 ZGC 低延迟
    "-XX:+ZGenerational"               # 分代 ZGC(更优吞吐)
    "-Xmx16g"                          # 最大堆内存
    "-Xms16g"                          # 初始堆内存(避免动态扩展)
    "-XX:AOTCache=app.aot"             # 使用 AOT 缓存
    "-XX:+AlwaysPreTouch"              # 启动时预分配内存
    "-XX:MaxGCPauseMillis=1"           # 目标停顿时间
    "-XX:+UseStringDeduplication"      # 字符串去重
    "--enable-preview"                 # 启用预览特性
    "-XX:+PrintCommandLineFlags"       # 打印最终参数
)

java "${JAVA_OPTS[@]}" -jar app.jar

7.3 监控与诊断

import java.lang.management.*;
import com.sun.management.HotSpotDiagnosticMXBean;

public class MonitoringExample {
    
    public static void main(String[] args) throws Exception {
        // GC 信息
        var gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
        for (var bean : gcBeans) {
            System.out.println("GC: " + bean.getName());
            System.out.println("  Collection count: " + bean.getCollectionCount());
            System.out.println("  Collection time: " + bean.getCollectionTime() + "ms");
        }
        
        // 内存使用
        var memoryBean = ManagementFactory.getMemoryMXBean();
        var heapUsage = memoryBean.getHeapMemoryUsage();
        System.out.println("Heap used: " + heapUsage.getUsed() / 1024 / 1024 + "MB");
        System.out.println("Heap max: " + heapUsage.getMax() / 1024 / 1024 + "MB");
        
        // 线程转储(JSON 格式,包含结构化并发层次)
        var diagBean = ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
        diagBean.dumpThreads("/tmp/threads.json", 
            HotSpotDiagnosticMXBean.ThreadDumpFormat.JSON);
    }
}

八、总结与展望

8.1 Java 26 关键改进总结

特性类型影响
HTTP/3 支持正式特性网络性能提升,协议现代化
AOT 对象缓存(跨 GC)正式特性启动速度 + 低延迟兼得
G1 GC 吞吐量优化正式特性5-15% 吞吐量提升
结构化并发预览特性并发编程简化
原始类型模式匹配预览特性语言统一性提升
紧凑源文件正式特性初学者友好
移除 Applet API正式特性清理历史遗留
Remove 32-bit x86 Port正式特性简化维护

8.2 迁移建议

  1. HTTP 客户端升级:评估是否受益于 HTTP/3,逐步迁移
  2. AOT 缓存启用:为关键应用生成 AOT 缓存
  3. GC 选择:大内存 + 低延迟场景考虑 ZGC + AOT
  4. 预览特性试用:结构化并发、原始类型模式匹配值得尝试

8.3 未来展望

Java 27(预计 2026 年 9 月)可能带来:

  • 值类型(Value Types)的进一步推进
  • 更多 Amber 项目特性的稳定化
  • 性能的持续优化

Java 26 再次证明了 Java 生态的持续创新活力。无论是云原生时代对启动性能的要求,还是现代网络协议的演进,Java 都在积极拥抱变化,为开发者提供更强大的工具链。


参考资料

  1. OpenJDK JDK 26 项目主页
  2. JEP 517: HTTP/3 for the HTTP Client API
  3. JEP 516: Ahead-of-Time Object Caching with Any GC
  4. JEP 522: G1 GC: Improve Throughput by Reducing Synchronization
  5. JEP 525: Structured Concurrency (Sixth Preview)
  6. JEP 530: Primitive Types in Patterns (Fourth Preview)
  7. RFC 9114: HTTP/3
  8. RFC 9000: QUIC Protocol

本文约 12000 字,深入解析了 Java 26 的核心特性,包含大量实战代码示例。欢迎在评论区分享你的使用体验和问题。

复制全文 生成海报 Java HTTP/3 AOT G1 GC 性能优化

推荐文章

Go 中的单例模式
2024-11-17 21:23:29 +0800 CST
一些高质量的Mac软件资源网站
2024-11-19 08:16:01 +0800 CST
Vue3中如何处理路由和导航?
2024-11-18 16:56:14 +0800 CST
File 和 Blob 的区别
2024-11-18 23:11:46 +0800 CST
JavaScript设计模式:适配器模式
2024-11-18 17:51:43 +0800 CST
Rust 中的所有权机制
2024-11-18 20:54:50 +0800 CST
php 统一接受回调的方案
2024-11-19 03:21:07 +0800 CST
Nginx 防止IP伪造,绕过IP限制
2025-01-15 09:44:42 +0800 CST
Vue3中的v-model指令有什么变化?
2024-11-18 20:00:17 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
Vue3中如何处理组件间的动画?
2024-11-17 04:54:49 +0800 CST
程序员茄子在线接单