Skip to content
<

正式特性

【了解】ZGC 默认分代模式

Java 22 引入了分代 ⁢ZGC,但当时你需⁡要通过特殊的 JV⁡M 参数来启用它:

bash
java -XX:+UseZGC -XX:+UnlockExperimentalVMOptions -XX:+UseGenerationalZGC MyApp

而在 Java 23 中,分⁢代模式成为了 ZG⁡C 的默认行为。

虽然听起来只是个小改动,但这个改变的背后是大量的性能测试和实际应⁢用验证的结果。Oracle 的工程师们发现⁡,分代 ZGC 在绝大多数应用场景中都能带⁡来显著的性能改善,特别是在内存分配密集的应用中,性能提升可能达到数倍之多。

bash
# Java 23 中,ZGC 默认使用分代模式
java -XX:+UseZGC MyApp

# 如果需要使用非分代模式(不推荐)
java -XX:+UseZGC -XX:-UseGenerationalZGC MyApp

# 监控分代 ZGC 的性能
java -XX:+UseZGC -Xlog:gc*:gc.log MyApp

分代 ZGC 的性能对比

java
public class ZGCPerformanceExample {
    public static void main(String[] args) {
        System.out.println("测试分代 ZGC 性能");
        
        // 模拟大量短期对象分配
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < 1000000; i++) {
            // 创建短期对象(年轻代)
            List<String> tempList = new ArrayList<>();
            for (int j = 0; j < 100; j++) {
                tempList.add("临时字符串" + j);
            }
            
            // 偶尔创建长期对象(老年代)
            if (i % 10000 == 0) {
                staticList.add("长期字符串" + i);
            }
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("分配时间: " + (endTime - startTime) + "ms");
        System.out.println("长期对象数量: " + staticList.size());
    }
    
    private static final List<String> staticList = new ArrayList<>();
}

运行测试:

bash
# 使用分代 ZGC(默认)
java -XX:+UseZGC -Xmx4g ZGCPerformanceExample

# 使用非分代 ZGC(对比)
java -XX:+UseZGC -XX:-UseGenerationalZGC -Xmx4g ZGCPerformanceExample

分代 ZGC 的优势:

  • 更高的分配性能:年轻代分配更快
  • 更少的 GC 开销:大部分对象在年轻代就被回收
  • 更好的内存利用率:减少内存碎片
  • 保持低延迟:继承 ZGC 的低延迟特性

【了解】Markdown 文档注释

Java 23 支持在 JavaDo⁢c 中使用 Markdo⁡wn 语法:

java
/**
 * # 用户服务类
 * 
 * 这个类提供了用户管理的核心功能,包括:
 * 
 * - **创建用户**:注册新用户账户
 * - **查询用户**:根据ID或用户名查找
 * - **更新用户**:修改用户信息
 * - **删除用户**:注销用户账户
 * 
 * ## 使用示例
 * 
 * ```java
 * UserService service = new UserService();
 * User user = service.createUser("张三", "zhangsan@example.com");
 * ```
 * 
 * > **注意**:所有操作都需要适当的权限验证
 * 
 * @author 鱼皮
 * @version 1.0
 * @since Java 23
 */
public class UserService {
    
    /**
     * ## 创建新用户
     * 
     * 根据提供的信息创建一个新的用户账户。
     * 
     * ### 参数说明
     * 
     * | 参数 | 类型 | 描述 |
     * |------|------|------|
     * | name | String | 用户姓名,**不能为空** |
     * | email | String | 电子邮箱,必须是有效格式 |
     * 
     * ### 返回值
     * 
     * 返回创建成功的 `User` 对象,包含:
     * - 自动生成的用户ID
     * - 创建时间戳
     * - 默认的用户设置
     * 
     * @param name 用户姓名
     * @param email 电子邮箱
     * @return 新创建的用户对象
     * @throws IllegalArgumentException 当参数无效时抛出
     */
    public User createUser(String name, String email) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("用户姓名不能为空");
        }
        if (email == null || !isValidEmail(email)) {
            throw new IllegalArgumentException("无效的电子邮箱格式");
        }
        
        return new User(generateId(), name, email, Instant.now());
    }
    
    /**
     * ### 验证邮箱格式
     * 
     * 使用正则表达式验证邮箱格式是否有效:
     * 
     * ```regex
     * ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
     * ```
     * 
     * @param email 要验证的邮箱地址
     * @return `true` 如果格式有效,否则 `false`
     */
    private boolean isValidEmail(String email) {
        return email.matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");
    }
    
    private String generateId() {
        return "USER_" + System.currentTimeMillis();
    }
}

record User(String id, String name, String email, Instant createdAt) {}

生成的 JavaDoc 现在支持:

  • 标题:使用 # 语法
  • 列表:支持有序和无序列表
  • 表格:Markdown 表格语法
  • 代码块:语法高亮的代码块
  • 强调:粗体和斜体文本
  • 引用:使用 > 的引用块
  • 链接:Markdown 链接语法

【了解】弃用 sun.misc.Unsafe 中的内存访问方法

Java 23 将 sun.misc.Unsafe 中的内存访问方法标记为弃用:

java
// 这些方法在 Java 23 中被弃用
// sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
// unsafe.allocateMemory(1024);  // 弃用
// unsafe.freeMemory(address);   // 弃用
// unsafe.getInt(address);       // 弃用

// 推荐使用现代替代方案
import java.lang.foreign.*;

public class ModernMemoryAccess {
    public static void main(String[] args) {
        // 使用 Foreign Function & Memory API
        try (Arena arena = Arena.ofConfined()) {
            // 分配内存
            MemorySegment segment = arena.allocate(1024);
            
            // 写入数据
            segment.set(ValueLayout.JAVA_INT, 0, 42);
            segment.set(ValueLayout.JAVA_INT, 4, 24);
            
            // 读取数据
            int value1 = segment.get(ValueLayout.JAVA_INT, 0);
            int value2 = segment.get(ValueLayout.JAVA_INT, 4);
            
            System.out.println("值1: " + value1 + ", 值2: " + value2);
        } // 内存自动释放
        
        // 或者使用 VarHandle 进行原子操作
        int[] array = new int[10];
        VarHandle arrayHandle = MethodHandles.arrayElementVarHandle(int[].class);
        
        // 原子操作
        arrayHandle.setVolatile(array, 0, 100);
        int value = (int) arrayHandle.getVolatile(array, 0);
        boolean success = arrayHandle.compareAndSet(array, 0, 100, 200);
        
        System.out.println("原子操作结果: " + success + ", 新值: " + array[0]);
    }
}

迁移指南:

  • 内存分配:使用 Arena.allocate() 替代 allocateMemory()
  • 内存访问:使用 MemorySegment 替代直接内存操作
  • 原子操作:使用 VarHandle 替代 Unsafe 的 CAS 操作
  • 对象操作:使用反射 API 或方法句柄

预览特性

【了解】模式、instanceof 和 switch 中的原始类型(预览)

Java 23 扩展了模式匹配,支持原始类型:

java
// 需要启用预览功能
// javac --enable-preview --release 23 PrimitivePatternExample.java

public class PrimitivePatternExample {
    public static void main(String[] args) {
        processValue(42);
        processValue(3.14);
        processValue("Hello");
        processValue(true);
    }
    
    // 在 switch 中使用原始类型模式
    public static void processValue(Object value) {
        switch (value) {
            case int i when i > 0 -> System.out.println("正整数: " + i);
            case int i when i == 0 -> System.out.println("零");
            case int i -> System.out.println("负整数: " + i);
            case double d when d > 1.0 -> System.out.println("大于1的浮点数: " + d);
            case double d -> System.out.println("小于等于1的浮点数: " + d);
            case boolean b -> System.out.println("布尔值: " + b);
            case String s -> System.out.println("字符串: " + s);
            default -> System.out.println("其他类型");
        }
    }
    
    // 在 instanceof 中使用原始类型模式
    public static String analyzeNumber(Object obj) {
        if (obj instanceof int i && i % 2 == 0) {
            return "偶数: " + i;
        } else if (obj instanceof int i) {
            return "奇数: " + i;
        } else if (obj instanceof double d && d == Math.floor(d)) {
            return "整数形式的浮点数: " + d;
        } else if (obj instanceof double d) {
            return "小数: " + d;
        }
        return "不是数字";
    }
    
    // 处理数组
    public static void processArray(Object array) {
        switch (array) {
            case int[] ints when ints.length == 0 -> 
                System.out.println("空整数数组");
            case int[] ints when ints.length == 1 -> 
                System.out.println("单元素整数数组: " + ints[0]);
            case int[] ints -> 
                System.out.println("整数数组,长度: " + ints.length);
            case double[] doubles -> 
                System.out.println("浮点数组,长度: " + doubles.length);
            case String[] strings -> 
                System.out.println("字符串数组,长度: " + strings.length);
            default -> 
                System.out.println("其他类型的数组或非数组");
        }
    }
}

【了解】类文件 API(第二次预览)

Java 23 继续完善类文件 API:

java
import java.lang.classfile.*;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;

// 需要启用预览功能
public class ClassFileAPIExample {
    public static void main(String[] args) throws Exception {
        // 动态生成一个计算器类
        byte[] calculatorBytes = generateCalculatorClass();
        
        // 将生成的类文件写入磁盘
        java.nio.file.Files.write(
            java.nio.file.Paths.get("Calculator.class"),
            calculatorBytes
        );
        
        // 分析现有的类文件
        analyzeClassFile(calculatorBytes);
    }
    
    // 生成计算器类
    public static byte[] generateCalculatorClass() {
        return ClassFile.of().build(
            ClassDesc.of("Calculator"),
            classBuilder -> {
                // 添加 add 方法
                classBuilder.withMethod(
                    "add",
                    MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int),
                    ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
                    methodBuilder -> {
                        methodBuilder.withCode(codeBuilder -> {
                            codeBuilder.iload(0)      // 加载第一个参数
                                      .iload(1)       // 加载第二个参数
                                      .iadd()         // 相加
                                      .ireturn();     // 返回结果
                        });
                    }
                );
                
                // 添加 multiply 方法
                classBuilder.withMethod(
                    "multiply",
                    MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int, ConstantDescs.CD_int),
                    ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
                    methodBuilder -> {
                        methodBuilder.withCode(codeBuilder -> {
                            codeBuilder.iload(0)      // 加载第一个参数
                                      .iload(1)       // 加载第二个参数
                                      .imul()         // 相乘
                                      .ireturn();     // 返回结果
                        });
                    }
                );
                
                // 添加 main 方法用于测试
                classBuilder.withMethod(
                    "main",
                    MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String.arrayType()),
                    ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
                    methodBuilder -> {
                        methodBuilder.withCode(codeBuilder -> {
                            // System.out.println("5 + 3 = " + Calculator.add(5, 3));
                            codeBuilder.getstatic(ClassDesc.of("java.lang.System"), "out", 
                                                ClassDesc.of("java.io.PrintStream"))
                                      .ldc("计算结果:")
                                      .invokevirtual(ClassDesc.of("java.io.PrintStream"), "println",
                                                   MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_String))
                                      .return_();
                        });
                    }
                );
            }
        );
    }
    
    // 分析类文件
    public static void analyzeClassFile(byte[] classBytes) throws Exception {
        ClassModel classModel = ClassFile.of().parse(classBytes);
        
        System.out.println("类名: " + classModel.thisClass().asInternalName());
        System.out.println("访问标志: " + classModel.flags().flagNames());
        
        System.out.println("\n方法列表:");
        for (MethodModel method : classModel.methods()) {
            System.out.println("- " + method.methodName().stringValue() + 
                             method.methodTypeSymbol().displayDescriptor());
            
            // 分析方法字节码
            method.code().ifPresent(codeModel -> {
                System.out.println("  字节码指令数: " + codeModel.elementList().size());
            });
        }
        
        System.out.println("\n字段列表:");
        for (FieldModel field : classModel.fields()) {
            System.out.println("- " + field.fieldName().stringValue() + 
                             " : " + field.fieldTypeSymbol().displayDescriptor());
        }
    }
}

【了解】Stream Gatherers(第二次预览)

Java 23 继续完善 S⁢tream Gat⁡herers:

java
import java.util.stream.Gatherer;
import java.util.stream.Gatherers;

// 需要启用预览功能
public class StreamGatherersExample {
    public static void main(String[] args) {
        // 数据处理示例
        List<String> logs = List.of(
            "2024-01-01 10:00:00 INFO 用户登录",
            "2024-01-01 10:01:00 ERROR 数据库连接失败",
            "2024-01-01 10:02:00 INFO 用户注册",
            "2024-01-01 10:03:00 WARN 内存使用过高",
            "2024-01-01 10:04:00 ERROR 服务器错误",
            "2024-01-01 10:05:00 INFO 用户登出"
        );
        
        // 1. 滑动窗口分析
        System.out.println("=== 滑动窗口分析 ===");
        logs.stream()
            .gather(Gatherers.windowSliding(3))
            .forEach(window -> {
                long errorCount = window.stream()
                    .filter(log -> log.contains("ERROR"))
                    .count();
                if (errorCount >= 2) {
                    System.out.println("检测到错误集中: " + window);
                }
            });
        
        // 2. 按批次处理
        System.out.println("\n=== 批次处理 ===");
        logs.stream()
            .gather(Gatherers.windowFixed(2))
            .forEach(batch -> {
                System.out.println("处理批次: " + batch.size() + " 条日志");
                batch.forEach(log -> System.out.println("  " + log));
            });
        
        // 3. 自定义 Gatherer - 错误统计
        System.out.println("\n=== 错误统计 ===");
        Gatherer<String, ?, ErrorStats> errorStatsGatherer = Gatherer.ofSequential(
            ErrorStats::new,  // 初始状态
            (stats, log, downstream) -> {
                stats.total++;
                if (log.contains("ERROR")) {
                    stats.errors++;
                } else if (log.contains("WARN")) {
                    stats.warnings++;
                } else if (log.contains("INFO")) {
                    stats.infos++;
                }
                return true;
            },
            (stats, downstream) -> {
                downstream.push(stats);
                return true;
            }
        );
        
        ErrorStats stats = logs.stream()
            .gather(errorStatsGatherer)
            .findFirst()
            .orElse(new ErrorStats());
        
        System.out.println("总计: " + stats.total + " 条日志");
        System.out.println("错误: " + stats.errors + " 条");
        System.out.println("警告: " + stats.warnings + " 条");
        System.out.println("信息: " + stats.infos + " 条");
        
        // 4. 自定义 Gatherer - 去重相邻元素
        System.out.println("\n=== 去重相邻元素 ===");
        List<Integer> numbers = List.of(1, 1, 2, 2, 2, 3, 1, 1, 4, 4);
        
        Gatherer<Integer, ?, Integer> deduplicateAdjacent = Gatherer.ofSequential(
            () -> new int[]{Integer.MIN_VALUE},  // 上一个值
            (state, element, downstream) -> {
                if (state[0] != element) {
                    state[0] = element;
                    downstream.push(element);
                }
                return true;
            }
        );
        
        List<Integer> deduplicated = numbers.stream()
            .gather(deduplicateAdjacent)
            .toList();
        
        System.out.println("原始: " + numbers);
        System.out.println("去重: " + deduplicated);
    }
    
    static class ErrorStats {
        int total = 0;
        int errors = 0;
        int warnings = 0;
        int infos = 0;
    }
}

【了解】模块导入声明(预览)

Java 23 引入了模块导入声明:

java
// 需要启用预览功能
// 传统的导入方式
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

// Java 23 的模块导入(预览)
import module java.base;  // 导入整个模块的公开 API

public class ModuleImportExample {
    public static void main(String[] args) {
        // 现在可以直接使用 java.base 模块中的所有公开类
        List<String> list = List.of("a", "b", "c");
        Map<String, Integer> map = Map.of("x", 1, "y", 2);
        Set<String> set = Set.of("1", "2", "3");
        
        String result = list.stream()
            .collect(Collectors.joining(", "));
        System.out.println(result);
    }
}

这个特性简化了大量使用某个模⁢块 API 的场景⁡,但需要谨慎使用以⁡避免命名冲突。

【了解】隐式声明类和实例主方法(第三次预览)

Java 23 继续简化简单⁢程序的编写:

java
// 最简单的 Hello World(预览)
void main() {
    println("Hello, World!");
}

// 或者使用字段
String message = "Hello from Java 23!";

void main() {
    println(message);
}

// 可以有多个方法
void main() {
    String greeting = createGreeting("Java 23");
    println(greeting);
}

String createGreeting(String version) {
    return "欢迎使用 " + version + "!";
}

这个特性让 Java 更适合⁢:        ⁡         ⁡

  • 教学:学生可以更容易地开始学习
  • 脚本:简单的自动化脚本
  • 原型:快速验证想法

孵化器特性

【了解】向量 API(第八次孵化器)

Java 23 继续改进向量 API:

java
import jdk.incubator.vector.*;

public class VectorMathExample {
    private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_PREFERRED;
    
    public static void main(String[] args) {
        // 向量化的数学运算
        double[] input = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0};
        double[] result = new double[input.length];
        
        // 计算 f(x) = x^2 + 2*x + 1
        vectorizedPolynomial(input, result);
        
        System.out.println("输入: " + Arrays.toString(input));
        System.out.println("结果: " + Arrays.toString(result));
        
        // 向量化的矩阵运算
        double[][] matrix1 = {{1, 2}, {3, 4}};
        double[][] matrix2 = {{5, 6}, {7, 8}};
        double[][] product = matrixMultiply(matrix1, matrix2);
        
        System.out.println("\n矩阵乘法结果:");
        for (double[] row : product) {
            System.out.println(Arrays.toString(row));
        }
    }
    
    // 向量化多项式计算
    public static void vectorizedPolynomial(double[] input, double[] result) {
        int upperBound = SPECIES.loopBound(input.length);
        
        for (int i = 0; i < upperBound; i += SPECIES.length()) {
            DoubleVector x = DoubleVector.fromArray(SPECIES, input, i);
            
            // f(x) = x^2 + 2*x + 1
            DoubleVector x2 = x.mul(x);                    // x^2
            DoubleVector twoX = x.mul(2.0);                // 2*x
            DoubleVector result_vec = x2.add(twoX).add(1.0); // x^2 + 2*x + 1
            
            result_vec.intoArray(result, i);
        }
        
        // 处理剩余元素
        for (int i = upperBound; i < input.length; i++) {
            double x = input[i];
            result[i] = x * x + 2 * x + 1;
        }
    }
    
    // 向量化矩阵乘法
    public static double[][] matrixMultiply(double[][] a, double[][] b) {
        int n = a.length;
        int m = b[0].length;
        int p = a[0].length;
        double[][] result = new double[n][m];
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j += SPECIES.length()) {
                DoubleVector sum = DoubleVector.zero(SPECIES);
                
                for (int k = 0; k < p; k++) {
                    DoubleVector aVec = DoubleVector.broadcast(SPECIES, a[i][k]);
                    DoubleVector bVec = DoubleVector.fromArray(SPECIES, b[k], j);
                    sum = aVec.fma(bVec, sum);
                }
                
                sum.intoArray(result[i], j);
            }
        }
        
        return result;
    }
}

总结

Java 23 最重要的改进是 ZGC 默认启用分代模式,这为大多数应用带来了显著的性能提升。Markdown 文档注释让 API 文档更加美观和易读,弃用 sun.misc.Unsafe 内存访问方法推动了向现代 API 的迁移。

预览特性中,原始类型的模式匹配扩展了类型安全的边界,S⁢tream Gatherers 为复⁡杂的数据处理提供了强大的工具。模块导⁡入声明简化了大型模块的使用,隐式声明类让简单程序的编写更加便捷。

虽然 Java 23 不是 LTS⁢ 版本,但它在性能优化⁡、开发体验和语言表达力⁡方面都有重要进展,为后续版本奠定了坚实基础。