使用静态编译构建Java微服务

当我们谈论Java性能时,绕不开两个关键技术:JIT(Just-In-Time)和AOT(Ahead-Of-Time)编译。前者让Java运行时越来越快,后者让Java应用秒级启动成为可能

在云原生时代,传统Java应用的启动慢、内存占用高的问题愈发突出。GraalVM的AOT编译技术为微服务场景带来了新的可能性。这篇文章将从编译原理出发,深入剖析JIT和AOT的技术细节,并通过实战案例展示如何基于静态编译构建高性能微服务

Java程序的执行流程

在深入JIT和AOT之前,先理解Java代码是如何执行的

传统的三阶段执行

.java源文件
    ↓ javac编译
.class字节码
    ↓ JVM加载
字节码解释执行 / JIT编译执行

机器码执行

第一阶段:静态编译(javac)

java
public class Demo {
    public int add(int a, int b) {
        return a + b;
    }
}

编译成字节码:

public int add(int, int);
  Code:
     0: iload_1        // 加载局部变量a
     1: iload_2        // 加载局部变量b
     2: iadd           // 整数加法
     3: ireturn        // 返回结果

javac只做语法分析和类型检查,生成的字节码是平台无关的中间代码

第二阶段:加载与解释执行

JVM启动后,类加载器将.class文件加载到内存,初期由解释器逐条执行字节码

解释器的工作方式(Hotspot的模板解释器):

cpp
// bytecodeInterpreter.cpp (简化)
while (true) {
    opcode = *pc++;  // 读取字节码指令
    switch (opcode) {
        case Bytecodes::_iadd:
            SET_STACK_INT(STACK_INT(-2) + STACK_INT(-1), -2);
            UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
            break;
        case Bytecodes::_iload_1:
            SET_STACK_INT(LOCALS_INT(1), 1);
            UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);
            break;
        // ... 数百个case分支
    }
}

每条字节码对应一段C++代码,通过巨大的switch-case分发执行。这种方式灵活但效率低

第三阶段:JIT编译执行

当方法被频繁调用,JIT编译器将热点代码编译成本地机器码,直接在CPU上执行,性能接近C++

JIT编译深度剖析

热点探测机制

JIT不会编译所有代码,只编译"热点代码"。Hotspot采用基于计数器的热点探测

两种计数器:

  1. 方法调用计数器(Invocation Counter):统计方法被调用次数
  2. 回边计数器(Back-Edge Counter):统计循环体执行次数
cpp
// methodData.hpp
class MethodCounters: public MetaspaceObj {
  private:
    int _interpreter_invocation_count;    // 解释器调用次数
    int _interpreter_throwout_count;      // 反优化次数
    int _invocation_counter;              // 方法调用计数
    int _backedge_counter;                // 回边计数

  public:
    void increment_invocation_count() {
        _invocation_counter++;
    }
};

触发编译的阈值

bash
# 客户端模式(C1编译器)
-XX:CompileThreshold=1500           # 默认1500次

# 服务端模式(C2编译器)
-XX:CompileThreshold=10000          # 默认10000次

当方法调用次数或循环次数超过阈值,JVM将方法提交给编译线程队列

分层编译策略

JDK 7引入分层编译(Tiered Compilation),结合C1和C2编译器的优势:

Level 0: 解释器

Level 1: C1编译(无profiling)

Level 2: C1编译(带调用计数profiling)

Level 3: C1编译(完整profiling)

Level 4: C2编译(激进优化)

C1编译器(Client Compiler)

  • 编译速度快,优化程度低
  • 适合客户端应用和启动阶段

C2编译器(Server Compiler)

  • 编译速度慢,优化激进
  • 适合长时间运行的服务端应用

启用分层编译(JDK 8+默认开启):

bash
-XX:+TieredCompilation
-XX:TieredStopAtLevel=4      # 最高编译层级

JIT优化技术实例

方法内联(Inlining)

JIT最重要的优化。将方法调用替换为方法体,消除调用开销

java
public int calculate(int x) {
    return add(x, 10) + multiply(x, 2);
}

private int add(int a, int b) {
    return a + b;
}

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

内联后(伪代码):

java
public int calculate(int x) {
    // 内联add方法
    int temp1 = x + 10;
    // 内联multiply方法
    int temp2 = x * 2;
    return temp1 + temp2;
}

Hotspot内联策略(bytecodeInfo.cpp):

cpp
bool InlineTree::should_inline(ciMethod* callee) {
    // 1. 方法太大不内联
    if (callee->code_size() > MaxInlineSize) {  // 默认35字节
        return false;
    }

    // 2. 调用次数太少不内联
    if (callee->interpreter_invocation_count() < MinInlineFrequency) {
        return false;
    }

    // 3. 递归深度限制
    if (inline_level() > MaxInlineLevel) {  // 默认9层
        return false;
    }

    return true;
}

控制内联的参数

bash
-XX:MaxInlineSize=35             # 最大内联方法大小
-XX:FreqInlineSize=325           # 频繁调用方法的内联大小
-XX:MaxInlineLevel=9             # 最大内联深度
-XX:InlineSmallCode=2000         # 已编译方法的最大大小

逃逸分析(Escape Analysis)

分析对象的作用域,如果对象不会"逃逸"到方法外,可以进行优化

标量替换

java
public void test() {
    Point p = new Point(1, 2);
    int sum = p.x + p.y;
}

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}

逃逸分析发现Point对象不会逃逸,JIT将对象分解为标量:

java
public void test() {
    int x = 1;
    int y = 2;
    int sum = x + y;
}

对象直接在栈上分配(甚至不分配),避免堆分配和GC压力

锁消除

java
public String concat(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
}

StringBuffer的方法都是synchronized的,但sb不会逃逸,JIT会消除锁:

cpp
// c2_MacroAssembler.cpp
if (LockingMode == LM_LIGHTWEIGHT && can_eliminate_lock(lock)) {
    // 锁消除:不生成加锁代码
    return;
}

启用逃逸分析(默认开启):

bash
-XX:+DoEscapeAnalysis
-XX:+EliminateAllocations        # 标量替换
-XX:+EliminateLocks              # 锁消除

循环优化

循环展开

java
// 原始代码
for (int i = 0; i < 100; i++) {
    sum += array[i];
}

// 展开后(伪代码)
for (int i = 0; i < 100; i += 4) {
    sum += array[i];
    sum += array[i + 1];
    sum += array[i + 2];
    sum += array[i + 3];
}

减少循环判断次数,提高指令级并行度

循环不变代码外提

java
// 原始代码
for (int i = 0; i < 100; i++) {
    result[i] = array[i] * factor.getValue();
}

// 优化后
int value = factor.getValue();  // 提到循环外
for (int i = 0; i < 100; i++) {
    result[i] = array[i] * value;
}

JIT编译的代价

预热时间(Warmup Time)

应用启动后需要一段时间才能达到最佳性能

java
public class JITWarmup {
    public static void main(String[] args) {
        for (int i = 0; i < 20000; i++) {
            compute(i);
            if (i % 5000 == 0) {
                long start = System.nanoTime();
                compute(1000);
                System.out.printf("Iteration %d: %d ns%n",
                    i, System.nanoTime() - start);
            }
        }
    }

    static int compute(int n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i * i;
        }
        return sum;
    }
}

输出示例:

Iteration 0: 245000 ns      ← 解释执行,慢
Iteration 5000: 89000 ns    ← C1编译
Iteration 10000: 12000 ns   ← C2编译
Iteration 15000: 11500 ns   ← 稳定

内存开销

  • 编译后的机器码占用CodeCache(默认240MB)
  • Profiling数据占用额外内存

反优化(Deoptimization)

JIT基于假设进行激进优化,假设失效时需要回退到解释执行

java
interface Service {
    void execute();
}

class FastService implements Service {
    public void execute() { /* 快速实现 */ }
}

// 运行一段时间后,JIT假设service永远是FastService,内联execute方法
Service service = new FastService();
for (int i = 0; i < 100000; i++) {
    service.execute();  // 内联为FastService.execute
}

// 突然换实现,触发反优化
service = new SlowService();
service.execute();  // 之前的优化失效,回退到解释执行

AOT编译

为什么需要AOT

传统JIT编译的痛点:

  1. 启动慢:需要预热才能达到最佳性能
  2. 内存占用高:JIT编译器、CodeCache、Profiling数据
  3. 不可预测:运行时编译带来性能抖动

云原生时代,这些问题更加突出:

  • Serverless:函数可能只运行几秒钟,预热时间占比过高
  • 容器化:频繁创建销毁实例,无法复用预热结果
  • 资源受限:小内存环境无法承受JIT开销

AOT编译将字节码提前编译成机器码,运行时无需编译:

.java源文件
    ↓ javac
.class字节码
    ↓ AOT编译器(GraalVM Native Image)
本地可执行文件(ELF/PE格式)

直接执行机器码

GraalVM Native Image原理

GraalVM是Oracle开发的高性能JVM和多语言运行时,其Native Image功能提供AOT编译

核心组件

  1. Graal编译器:用Java编写的JIT/AOT编译器,替代Hotspot的C2
  2. Substrate VM:轻量级运行时,替代完整的JVM
  3. Points-to分析:全程序静态分析,确定哪些代码会被执行

编译流程

字节码

静态分析(Points-to Analysis)
    ├─ 从main方法出发
    ├─ 分析所有可达代码
    └─ 构建完整的调用图

初始化分析(Build-time Initialization)
    ├─ 在编译期初始化类
    └─ 将初始化结果序列化到镜像

Graal编译器(AOT编译)
    ├─ 激进内联
    ├─ 逃逸分析
    └─ 生成优化的机器码

链接与打包
    └─ 生成独立可执行文件

关键技术:闭世界假设(Closed World Assumption)

Native Image假设编译时分析到的代码就是全部代码,运行时不会有新代码

这意味着:

  • ❌ 不支持动态类加载
  • ❌ 反射需要提前配置
  • ❌ 动态代理需要提前生成
  • ✅ 可以进行全局优化
  • ✅ 可以删除未使用代码

Native Image的配置

反射配置(reflect-config.json):

json
[
  {
    "name": "com.example.User",
    "allDeclaredFields": true,
    "allDeclaredMethods": true,
    "allDeclaredConstructors": true
  }
]

JNI配置(jni-config.json):

json
[
  {
    "name": "java.lang.String",
    "methods": [
      {"name": "charAt", "parameterTypes": ["int"]}
    ]
  }
]

资源配置(resource-config.json):

json
{
  "resources": {
    "includes": [
      {"pattern": "application.properties"},
      {"pattern": "templates/.*\\.html"}
    ]
  }
}

自动生成配置

使用Tracing Agent在运行时记录反射和资源访问:

bash
java -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image \
     -jar myapp.jar

运行典型场景后,Agent生成配置文件

案例:构建基于AOT的微服务

技术栈选择

我们用Spring Boot 3 + GraalVM构建一个高性能微服务

环境准备

bash
# 安装GraalVM
curl -L https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.0/graalvm-ce-java17-linux-amd64-22.3.0.tar.gz | tar xz
export JAVA_HOME=/path/to/graalvm
export PATH=$JAVA_HOME/bin:$PATH

# 验证
java -version
# openjdk version "17.0.5" 2022-10-18
# OpenJDK Runtime Environment GraalVM CE 22.3.0 (build 17.0.5+8-jvmci-22.3-b08)

native-image --version
# GraalVM 22.3.0 Java 17 CE

项目搭建

pom.xml

xml
<project>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Native支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- JSON处理 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <!-- Redis客户端(演示数据访问) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Native Image插件 -->
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

业务代码实现

领域模型

java
public record Product(
    String id,
    String name,
    BigDecimal price,
    int stock
) {}

public record OrderRequest(
    String productId,
    int quantity,
    String userId
) {}

public record OrderResponse(
    String orderId,
    String status,
    BigDecimal totalAmount
) {}

使用Record简化代码,GraalVM完美支持

服务层

java
@Service
public class OrderService {
    private final StringRedisTemplate redis;
    private final RestTemplate restTemplate;

    public OrderService(StringRedisTemplate redis, RestTemplate restTemplate) {
        this.redis = redis;
        this.restTemplate = restTemplate;
    }

    public OrderResponse createOrder(OrderRequest request) {
        // 1. 检查库存(模拟Redis查询)
        String stockKey = "product:" + request.productId() + ":stock";
        String stockStr = redis.opsForValue().get(stockKey);

        if (stockStr == null) {
            throw new BusinessException("Product not found");
        }

        int stock = Integer.parseInt(stockStr);
        if (stock < request.quantity()) {
            throw new BusinessException("Insufficient stock");
        }

        // 2. 扣减库存
        redis.opsForValue().decrement(stockKey, request.quantity());

        // 3. 调用支付服务(模拟HTTP调用)
        PaymentRequest payment = new PaymentRequest(
            request.userId(),
            calculateAmount(request)
        );

        PaymentResponse paymentResp = restTemplate.postForObject(
            "http://payment-service/api/pay",
            payment,
            PaymentResponse.class
        );

        // 4. 生成订单
        String orderId = UUID.randomUUID().toString();

        return new OrderResponse(
            orderId,
            "COMPLETED",
            calculateAmount(request)
        );
    }

    private BigDecimal calculateAmount(OrderRequest request) {
        // 简化:实际应查询商品价格
        return BigDecimal.valueOf(99.99).multiply(BigDecimal.valueOf(request.quantity()));
    }
}

控制器

java
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping
    public ResponseEntity<OrderResponse> createOrder(@RequestBody OrderRequest request) {
        OrderResponse response = orderService.createOrder(request);
        return ResponseEntity.ok(response);
    }

    @GetMapping("/{id}")
    public ResponseEntity<Order> getOrder(@PathVariable String id) {
        // 实现查询逻辑
        return ResponseEntity.ok(/* ... */);
    }
}

异常处理

java
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
        return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body(new ErrorResponse(ex.getMessage()));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception ex) {
        return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(new ErrorResponse("Internal server error"));
    }
}

record ErrorResponse(String message) {}

Native Image配置

application.properties

properties
spring.application.name=order-service
server.port=8080

# Redis配置
spring.data.redis.host=localhost
spring.data.redis.port=6379

# Actuator端点
management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.show-details=always

native-image配置(src/main/resources/META-INF/native-image/native-image.properties):

properties
Args = --initialize-at-build-time=org.slf4j \
       --initialize-at-run-time=io.netty \
       -H:+ReportExceptionStackTraces \
       -H:+PrintClassInitialization \
       --enable-http \
       --enable-https \
       --no-fallback

Spring AOT处理

Spring Boot 3内置AOT引擎,自动生成Hint:

java
@Configuration
public class MyRuntimeHints implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // 手动注册反射Hint
        hints.reflection().registerType(
            OrderRequest.class,
            MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
            MemberCategory.DECLARED_FIELDS
        );

        // 注册资源
        hints.resources().registerPattern("templates/*.html");

        // 注册序列化
        hints.serialization().registerType(Product.class);
    }
}

构建Native Image

Maven构建

bash
# 运行测试并生成AOT资源
mvn clean test

# 构建Native Image
mvn -Pnative native:compile

# 或使用Spring Boot插件
mvn spring-boot:build-image

构建过程:

[1/8] Initializing...                                            (5.2s @ 0.25GB)
[2/8] Performing analysis...  [*******]                         (42.3s @ 2.10GB)
[3/8] Building universe...                                       (6.1s @ 2.45GB)
[4/8] Parsing methods...      [***]                             (8.4s @ 2.21GB)
[5/8] Inlining methods...     [***]                             (3.2s @ 2.67GB)
[6/8] Compiling methods...    [********]                        (67.8s @ 3.12GB)
[7/8] Creating image...                                          (7.9s @ 2.89GB)
[8/8] Writing image...                                           (2.3s @ 2.45GB)

Finished generating 'order-service' in 2m 23s.

产物对比

bash
# JAR包
-rw-r--r--  1 user  staff   45M  order-service.jar

# Native Image
-rwxr-xr-x  1 user  staff   78M  order-service 包含运行时

虽然Native Image更大,但它是完整的可执行文件,无需JVM

性能对比测试

启动时间

bash
# JVM模式
$ time java -jar order-service.jar
Started OrderServiceApplication in 3.247 seconds

real    0m3.891s
user    0m8.234s
sys     0m0.521s

# Native Image模式
$ time ./order-service
Started OrderServiceApplication in 0.087 seconds

real    0m0.124s
user    0m0.042s
sys     0m0.038s

启动速度提升30倍以上

内存占用

bash
# JVM模式(启动后)
$ ps aux | grep order-service
USER   PID  %CPU %MEM      VSZ    RSS
user  1234  120  3.5  7234816  451234 ~440MB

# Native Image模式(启动后)
$ ps aux | grep order-service
USER   PID  %CPU %MEM      VSZ    RSS
user  5678   15  0.8  1234567   98234 ~96MB

内存占用减少4-5倍

吞吐量测试

使用wrk进行压测:

bash
wrk -t4 -c100 -d30s --latency \
    -s post.lua http://localhost:8080/api/orders

结果:

JVM模式(预热后):
Requests/sec:   8234.56
Latency:        12.14ms (avg)

Native Image模式:
Requests/sec:   7891.23
Latency:        12.67ms (avg)

吞吐量相近,JIT编译后的性能略优于AOT

冷启动性能

模拟Serverless场景,测试前100个请求的延迟:

python
import requests
import time

times = []
for i in range(100):
    start = time.time()
    resp = requests.post('http://localhost:8080/api/orders', json={
        'productId': 'p123',
        'quantity': 1,
        'userId': 'u456'
    })
    times.append((time.time() - start) * 1000)

print(f"P50: {sorted(times)[50]:.2f}ms")
print(f"P99: {sorted(times)[99]:.2f}ms")

结果:

JVM模式:
P50: 45.23ms  ← 预热中,性能差
P99: 234.56ms

Native Image模式:
P50: 12.34ms  ← 立即达到最佳性能
P99: 23.45ms

部署与监控

Docker镜像

dockerfile
# JVM镜像(基于eclipse-temurin)
FROM eclipse-temurin:17-jre
COPY target/order-service.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
# 镜像大小: ~280MB

# Native Image镜像(基于distroless)
FROM gcr.io/distroless/base
COPY target/order-service /app
ENTRYPOINT ["/app"]
# 镜像大小: ~85MB

Native Image镜像更小、更安全(无shell、无包管理器)

Kubernetes部署

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: order-service
        image: myregistry/order-service:native
        resources:
          requests:
            memory: "128Mi"    # Native Image内存需求低
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 1  # 启动快,无需长时间等待
          periodSeconds: 10

监控指标

Spring Boot Actuator在Native Image中完美工作:

bash
curl http://localhost:8080/actuator/metrics/jvm.memory.used

{
  "name": "jvm.memory.used",
  "measurements": [
    {"statistic": "VALUE", "value": 98234567}  # ~94MB
  ],
  "availableTags": [
    {"tag": "area", "values": ["heap", "nonheap"]},
    {"tag": "id", "values": ["Survivor Space", "Eden Space"]}
  ]
}

即使是Native Image,也保留了JVM的监控能力(Substrate VM提供)

JIT 和 AOT 如何选择

性能对比总结

指标JIT(HotSpot)AOT(GraalVM Native Image)
启动时间慢(秒级)快(毫秒级)
预热时间需要(分钟级)无需预热
峰值性能高(激进优化)中等(保守优化)
内存占用高(JVM开销)低(轻量运行时)
镜像大小大(JRE+JAR)中等(单一可执行文件)
运行时灵活性高(动态加载)低(闭世界假设)

适用场景

选择JIT的场景

  1. 长期运行的服务:有充足预热时间,峰值性能更重要
  2. 计算密集型应用:需要JIT的激进优化(科学计算、大数据处理)
  3. 复杂业务逻辑:大量使用反射、动态代理、字节码生成
  4. 成熟生态系统:依赖不支持Native Image的库

选择AOT的场景

  1. Serverless函数:冷启动时间决定用户体验
  2. 微服务:快速扩缩容,内存成本敏感
  3. 命令行工具:用户期望即时响应
  4. 资源受限环境:IoT设备、边缘计算

混合策略

GraalVM支持PGO(Profile-Guided Optimization):

bash
# 1. 运行应用收集Profile
java -Dgraal.PGOInstrument=profile.iprof -jar app.jar

# 2. 运行典型场景
curl http://localhost:8080/api/...

# 3. 基于Profile构建Native Image
native-image --pgo=profile.iprof -jar app.jar

结合JIT的运行时信息和AOT的预编译优势

未来展望

Project Leyden

Oracle正在开发的JDK新特性,目标是"shifting and constraining computation"

核心理念

  1. CDS(Class Data Sharing)增强:共享类元数据,加速启动
  2. AOT缓存:缓存JIT编译结果,跨进程复用
  3. 静态镜像:类似Native Image但保留动态性
bash
# 生成静态镜像
java -XX:ArchiveClassesAtExit=app.jsa -jar app.jar

# 使用静态镜像启动
java -XX:SharedArchiveFile=app.jsa -jar app.jar
# 启动时间减少50%+

预计在JDK 23-25正式发布

OpenJDK的AOT复兴

虽然JDK 11引入的实验性AOT在JDK 17中被移除,但社区正在重新评估:

  • GraalVM成为OpenJDK官方子项目
  • Native Image技术回流到OpenJDK主线
  • 更好的工具链集成

WebAssembly前景

将Java编译成Wasm,实现真正的"一次编写,到处运行":

bash
# 未来可能的工作流
javac Hello.java
wasm-java Hello.class -o hello.wasm
wasmtime hello.wasm

总结

JIT和AOT不是对立关系,而是互补的技术

JIT的价值

  • 根据实际运行情况优化,峰值性能更高
  • 灵活应对运行时变化
  • 适合长期运行的服务端应用

AOT的价值

  • 极致的启动速度和内存效率
  • 可预测的性能表现
  • 适合云原生和边缘计算场景

随着GraalVM的成熟和Project Leyden的推进,Java在不同场景下都将拥有更优的性能表现。选择合适的技术,让Java应用跑得更快、更省资源

对于微服务架构,Native Image已经是生产可用的选择。如果你的服务启动时间超过5秒、内存占用超过500MB,不妨尝试一下AOT编译,可能会带来意想不到的收益

Dive Into -- Redis中的HyperLogLog
利用RAG低成本构建一个专业AI助手