性能优化-升级JDK
5.10 升级JDK
Java每个版本迭代,除了新的语言特性外,还有安全提升,JVM和SDK的性能提升,比如JDK7之前生成随机函数是Random类,多线程下使用单个Random实例生成随机数时候,多个线程同时计算随机数计算新的种子时候多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,这是会降低并发性能的,JDK7增加了ThreadLocalRandom,有更好的性能
可以运行RandomTest比较俩着的性能,RandomTest使用50个并发线程测试
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Threads(50)
@Fork(1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class RandomTest {
Random random = new Random();
@Benchmark
public int random() {
return random.nextInt(50);
}
@Benchmark
public int localRandom() {
ThreadLocalRandom random = ThreadLocalRandom.current();
return random.nextInt(50);
}
}从测试结果来看,ThreadLocalRandom性能远远超过Random
Benchmark Mode Score Units
c.i.c.c.RandomTest.localRandom avgt 109.176 ns/op
c.i.c.c.RandomTest.random avgt 7137.961 ns/opJava每次升级都有较多的性能改,下表列出了我在项目中遇到有显著性能提升的升级
| API | 描述 | 新版本 | 性能提升 |
|---|---|---|---|
| Random | 生成随机函数 | JDK8,采用ThreadLocalRandom | 7000% |
| DateFomart | 日期格式化 | JDK8, 采用DateTimeFormatter | 300% |
| SSLEngine | SSL握手,用于网络安全认证 | JDK8最新版本,优化加密解密计算,预分配了缓存用于存放解码内容 | 200% |
| BASE64Encoder | 二进制和Base64编解码 | JDK8使用java.util.Base64 | 500% |
| String | 字符串拼接 | JDK9 不再编译时期调用StringBuilder,改成了在运行时期根据拼接内容动态决定如何拼接 | 略有提升 |
| HTTP Client | HTTP客户端 | JDK17有较大性能提升,之前则采用第三方的,如Apache Client或者HttpOK | 略有提升 |
| 线程 | JDK的Thread映射成内核线程pthread,上下文切换开销较大。 | Java21 实现虚拟线程,虚拟线程是由JVM管理的轻量级线程。虚拟线程非常适合I/O密集型任务或需要大量并行性的任务。 | 单个JVM可以创建数百万的虚拟线程 |
如下是使用虚拟线程,可以轻松创建数十万的并发计算任务
Runnable runnable = () -> {
//完成某些计算
};
try (ExecutorService pool = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 1000000; i++) {
pool.submit(runnable);
}
}下表列出了JEP 提案中提到的Java各个版本的性能优化,数据来源JEP官网,并使用AI总结成如下表格。JEP主要是对JVM,JIT,并发,以及标准库的性能优化。
| LTS | 版本 | GA 时间 | 核心性能主线 |
|---|---|---|---|
| LTS | 8 | 2014-03 | 并行 GC 吞吐基线、压缩指针 |
| — | 9 | 2017-09 | GC 默认切换 G1 + 内存****减半(紧凑字符串) |
| — | 10 | 2018-03 | G1 Full GC 并行化 |
| LTS | 11 | 2018-09 | 低延迟 GC 入局(ZGC/Epsilon)+ JFR 开源 |
| — | 12 | 2019-03 | Shenandoah + 默认 CDS |
| — | 13 | 2019-09 | 动态 CDS + ZGC 内存归还 |
| — | 14 | 2020-03 | ZGC 生产级、G1 NUMA、移除 CMS |
| — | 15 | 2020-09 | ZGC/Shenandoah 转正 |
| — | 16 | 2021-03 | 弹性元空间 + ZGC 并发栈 |
| LTS | 17 | 2021-09 | 清理(移除 AOT/Graal)、PRNG 增强 |
| — | 18 | 2022-03 | 夯实期(UTF-8 默认等) |
| — | 19 | 2022-09 | 并发范式转折点:虚拟线程预览 |
| — | 20 | 2023-03 | 演进期 |
| LTS | 21 | 2023-09 | 分代 ZGC + 虚拟线程转正 |
| — | 22 | 2024-03 | FFM 转正、G1 区域钉扎 |
| — | 23 | 2024-09 | ZGC 分代默认 |
| — | 24 | 2025-03 | 虚拟线程去钉扎 + 紧凑对象头 + AOT 类加载 |
以Java8为例子,优化了GC、内存使用和标准库
GC 与****内存
| 项 | 机制 | 要点 |
|---|---|---|
| 默认并行收集器 | Parallel Scavenge(新生代) + Parallel Old(老年代),多线程并行 STW | 吞吐优先;停顿随堆增长恶化 |
| 压缩指针 | -XX:+UseCompressedOops(64 位默认) | 引用 8B→4B,堆<32G 时内存与缓存均受益 |
| 8u131/8u191 容器感知 | -XX:+UseContainerSupport(8u191 默认) | 容器内正确识别 CPU/内存配额,避免 OOM |
标准库
| 项 | 机制 | 要点 |
|---|---|---|
| JEP 192 字符串去重(8u20) | G1 下 -XX:+UseStringDeduplication:重复 String 共享底层 char[] | 显式开启;适合字符串驻留多的应用 |
| Lambda/Stream | 集合管道化编码 | 间接:减少手写循环开销,便于 JIT 优化 |
以Java21 为例子,优化了GC和并发
GC
| 项 | 机制 | 收益 |
|---|---|---|
| JEP 439 分代 ZGC | 年轻/老年代分离,小堆(≤16G)收益最大 | 吞吐≈G1,停顿亚毫秒(官方:相比非分代 ZGC 吞吐提升 2~4 倍量级) |
并发
| 项 | 机制 | 收益 |
|---|---|---|
| JEP 444 虚拟线程转正 | 生产可用 | 百万级线程(官方 demo 100 万虚拟线程);I/O 密集应用吞吐提升数倍至一个数量级 |
注意:升级JDK版本有风险,需要充分的测试。尤其是跟安全相关的功能,JDK可能因为安全原因不再与旧版本兼容,如JDK每次大版本更新,一些被认为不安全的加密算法就会下线。如果客户端依然使用老的加加密算法,可能导致客户端访问失败
JDK升级有可能意味着 框架或者工具库可能不兼容,比如JDK17不再支持java.ee包,改成了jakarta.ee,必须同步升级开发框架和依赖的工具,这可能进一步引入新的风险
