升级指南之JDK 11+ 新特性和AJDK
写在前面:
JDK已经如火如荼的更新到了JDK 19,集团内也在推AJDK 11的升级,升级已然是一个大趋势。本文主要是对体育营销场景升级JDK 11,进行了整理与总结,希望对大家有所帮助。
JDK版本概述
Java 版本新特性
JDK 9
模块化
/**
* Defines the JDBC API.
*
* @uses java.sql.Driver
*
* @moduleGraph
* @since 9
*/
// 声明模块
module java.sql {
// 声明依赖模块, transitive修饰符会导致依赖于当前模块的其他模块具有隐式依赖性。
requires transitive java.logging;
requires transitive java.transaction.xa;
requires transitive java.xml;
// 声明哪些包是可以被其它模块访问
exports java.sql;
exports javax.sql;
// 使用语句(uses statement)和提供语句(provides statement)实现其服务
// 使用语句可以指定服务接口的名字,当前模块就会发现它,使用 java.util.ServiceLoader类进行加载
uses java.sql.Driver;
}
接口支持私有方法
public interface TestInterface{
default void method(){ init(); }
default void anotherMethod(){ init(); }
private void init(){ System.out.println("Initializing");}
}
新增Stream API & 集合工厂方法
takeWhile
// 输出 => abc
Stream.of("a","b","c","","e","f").takeWhile(s->!s.isEmpty())
.forEach(System.out::print);
dropWhile
// 输出 => ef
Stream.of("a","b","c","","e","f").dropWhile(s-> !s.isEmpty())
.forEach(System.out::print);
ofNullable
// 输出 => 0
System.out.println(Stream.ofNullable(null).count());
集合工厂方法
// 通过java.util.Set 创建 不可变 的集合实例
Set<String> set = Set.of("A", "B", "C");
// 通过java.util.List 创建 不可变 的集合实例
List<String> list = List.of("A", "B", "C");
// 通过k1,v1,k2,v2,...,形式创建
Map<String, String> map = Map.of("A","V1","B","v2","C","v3");
// 通过 Map.entry 形式创建
Map<Integer, String> map1 = Map.ofEntries (
Map.entry(1, "v1"),
Map.entry(2, "v2"),
Map.entry(3, "v3"));
改进版 Try-With Resources
static String readData(String message) throws IOException {
Reader reader = new StringReader(message);
BufferedReader br = new BufferedReader(reader);
// 不需要重新声明变量
try (br) {
return br.readLine();
}
}
G1 成为默认垃圾收集器
JDK 10
APPCDS 应用程序类数据共享
多线程并行 GC
局部变量类型推断
var list = new ArrayList<String>();
list.add("hello,world!");
System.out.println(list);
ArrayList<String> list = new ArrayList();
list.add("hello,world!");
System.out.println(list);
线程-局部管控
基于Java的实验性JIT编译器Graal
JDK 11
String 增强
// 判断字符串是否为空
" ".isBlank(); // true
// 去除字符串首尾空格
" JDK11 ".strip();// "JDK11"
// 去除字符串首部空格
" JDK11 ".stripLeading(); // "JDK11 "
// 去除字符串尾部空格
" JDK11 ".stripTrailing(); // " JDK11"
// 重复字符串多少次
"JDK11 ".repeat(3); // "JDK11 JDK11 JDK11 "
// 返回由行终止符分隔的字符串集合
"A\nB\nC".lines().count(); // 3
支持TLS 1.3 协议
HTTP Client
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.build();
// 异步
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
// 同步
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Epsilon:低开销垃圾回收器
飞行记录器:JFR
JDK 12 ~ 14
G1 收集器优化
可中止的混合收集集合 :为了达到用户提供的停顿时间目标,通过把要被回收的区域集(混合收集集合)拆分为强制和可选部分,使 G1 垃圾回收器能中止垃圾回收过程。G1 可以中止可选部分的回收以达到停顿时间目标。
及时返回未使用的已分配内存 :由于 G1 尽量避免完整的 GC,并且仅基于 Java 堆占用和分配活动来触发并发周期,因此在许多情况下,除非从外部强制执行,否则它不会返还 Java 堆内存。JDK 12增强了 G1 GC,可以在空闲时自动将 Java 堆内存返回给操作系统。
SocketAPI 重构
动态 CDS 存档
增强 switch
String result = switch (day) {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" -> "工作日";
case "weekend" -> "周末";
default -> {
// yield 关键字 yield的是JDK13后的一个新特性,它主要的作用是进行程序的局部返回
yield "unknown";
}
};
System.out.println(result);
移除 CMS 垃圾收集器
JDK 15 ~ 16
文本块
String content = """
JDK 10
JDK 11
JDK 12
""";
System.out.println(content);
JDK 10
JDK 11
JDK 12
Hidden Classes(隐藏类)
instanceof 模式匹配
// JDK 16之前
if (obj instanceof String) {
// 强制转换后使用
String str = (String)o;
... use str ...
}
// JDK 16
if (o instanceof String str) {
// 直接使用str变量
... use s ...
}
记录类
// 定义记录类
public record Person(String name, Integer age) {
}
// ======
// 使用
Person person = new Person("张三", 16);
System.out.println(person);
ZGC 并发线程堆栈处理
JDK 17
增强的伪随机数生成器
RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
// 使用时间戳作为随机数种子
RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
// 生成随机数
randomGenerator.nextInt(10));
密封类
// 比如现在要限制 Person类 只能被这三个类继承,不能被其他类继承,可以这么做
// 添加sealed修饰符,permits后面跟上只能被继承的子类名称
public sealed class Person permits Teacher, Worker, Student{ } //人
// 子类可以被修饰为 final
final class Teacher extends Person { }//教师
// 子类可以被修饰为 non-sealed,此时 Worker类就成了普通类,谁都可以继承它
non-sealed class Worker extends Person { } //工人
// 任何类都可以继承Worker
class AnyClass extends Worker{}
移除实验性的 AOT 和 JIT 编译器
删除 Applet API
AJDK11专有特性
应用启动加速 EagerAppCDS
代码预热技术JWarmup2
AppAOT
QuickStart框架
VectorAPI
为什么升级?
目前大多数应用用的还是JDK 8,不知道高版本的JDK能带来什么收益,所以没有动力去升级,我理解升级JDK版本带来的收益核心有三点:
性能收益,从已升级的应用的数据来看,性能收益还是不错的,后面详细介绍
新特性, 高版本有更多的语法和功能供我们使用
业界主流趋势 springboot 、netty、Kafka这些常用的框架在高版本里已不再支持低版本JDK(JDK8及以下)
如何选择版本?
AJDK11+G1升级收益
体育营销中心
其它案例
SPECjbb2015 JDK8/11性能分析
TPP
MTOP
升级指南
常用软件升级
IntelliJ IDEA: 2018.2[2]
Eclipse: Photon 4.9RC2 with Java 11 plugin[3]
Maven: 3.5.0
compiler plugin: 3.8.0
surefire and failsafe: 2.22.0
Gradle: 5.0[4]
常用框架支持JDK11的最低版本:
ASM : 7.0 Spring : 5.0 Guice : 4.2 guava : 19.0
本地环境升级
本地开发可以在这里[5]选择合适的版本进行下载 (AJDK11只支持Linux操作系统)
项目配置新版本JDK 11
Linux部署环境升级
修改 ${app}.release 文件 的 baseline.jdk
# 构建打包使用jdk版本, aone编译机器上未安装最新版本的ajdk,
# 可以用已安装的ajdk版本进行编译,实际运行用最新版本的ajdk,最新版本的ajdk有较多新特性
baseline.jdk=ajdk11_11.0.14.13_fp2
# 升级maven版本,建议用amaven, amaven有代码编译加速的功能
build.tools.maven=amaven3.5.0
dockerfile修改
# 安装ajdk11 最新版本的jdk可以在这里查看 http://yum.tbsite.net/taobao/7/x86_64/current/ajdk11/
rpm -ivh --nodeps "http://yum.tbsite.net/taobao/7/x86_64/current/ajdk11/ajdk11-11.0.16.15_fp1-20220929100209.alios7.x86_64.rpm" && \
# 把java目录软链到 /opt/taobao/java 目录,ajdk8自动连接,ajdk11 需要手动软链
rm -rf /opt/taobao/java && ln -s /opt/taobao/install/ajdk11-11.0.16.15_fp1 opt/taobao/java && \
setenv.sh 脚本修改
# 去掉cms相关参数,删除下面两行
SERVICE_OPTS="${SERVICE_OPTS} -XX:+UseConcMarkSweepGC -XX:CMSMaxAbortablePrecleanTime=5000"
SERVICE_OPTS="${SERVICE_OPTS} -XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly"
遇到的问题
aone编译机器上未安装相应的jdk
aone发布编译报错
原因是目前ajdk 11 只在aone编译机器上安装了部分版本,未升级到最新版本
解决方案:
编译AJDK版本可以用aone编译机器上已安装的版本
申请请安装新版本的ajdk
应用启动报错 io.netty.util.internal.PlatformDependent0 - direct buffer constructor: unavailable
报错截图
解决方式:setenv.sh 脚本增加如下参数。
SERVICE_OPTS="${SERVICE_OPTS} -Dio.netty.tryReflectionSetAccessible=true --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED"
QuickStart框架集成
总结
[1]https://newrelic.com/resources/report/2022-state-of-java-ecosystem
[2]https://blog.jetbrains.com/idea/2018/06/java-11-in-intellij-idea-2018-2/
[3] https://blog.codefx.org/https://marketplace.eclipse.org/content/java-11-support-eclipse-photon-49
[4]https://docs.gradle.org/5.0/release-notes.html#java-11-runtime-support
微信扫码关注该文公众号作者