一帆磨砺

生活所迫,一叶孤舟

0%

Java版本特性-JDK11

引用参考文档链接

  1. jdk11特性列表
  2. nest class和inner class的区别
  3. JDK11中增加了一个常量池类型:CONSTANT_Dynamic
  4. new-jvm-features-jdk-11
  5. hands-on-java-constantdynamic

写在开头

  1. 直接影响编码的功能几乎没有,大多数都是幕后工作的更新
  2. ZGC是一个核心内容,不过JDK11中是实验版
  3. JFR的加入方便了性能监控,建议上手实际操作几次,非常好用
  4. 加密算法的几个新增实现未进行深入了解(未涉足领域,功力不足)

内部类(嵌套类)的权限控制(Nest-Based Access Control)

Introduce nests, an access-control context that aligns with the existing notion of nested types in the Java programming language. Nests allow classes that are logically part of the same code entity, but which are compiled to distinct class files, to access each other’s private members without the need for compilers to insert accessibility-broadening bridge methods.

原文的开头介绍说自该版本起,内部类之间访问私有属性不用再通过编译器生成accessibility-broadening bridge methods。然后我阅读整篇文档都没有举例说明这个是啥,并且又引入了新的概念InnerClassesEnclosingMethod,越看越迷糊,百度也没有比较好的文章去说明这个特性,不过还好搜到了nest class和inner class的区别,但是光明白这个也没用,还好之前看《深入理解Java虚拟机》时了解到了java的class文件反编译汇编的概念,于是尝试了一下

Demo类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 //https://openjdk.org/jeps/181
public class NestClass {
public static class NestClass_1 {
public void func() {
NestClass_2 n2 = new NestClass_2();
System.out.println("NestClass_1:func:NestClass_2:name:" + n2.name);
NestClass nestClass = new NestClass();
InnerClass i1 = nestClass.new InnerClass();
System.out.println("NestClass_1:func:InnerClass:name:" + i1.name);
}
}

public static class NestClass_2 {
private String name = "NestClass_2";
}

public class InnerClass {
private String name = "InnerClass";
}
}

该类通过JDK8和JDK11分别得到两套class文件,使用javap -c class文件名命令去得到反编译结果,可直接查看对比解读

反编译结果

JDK8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
 jan@JanWarlendeMacBook-Pro feature % javap -c NestClass.class 
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass {
public com.janwarlen.feature.NestClass();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass\$NestClass_1.class
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass$NestClass_1 {
public com.janwarlen.feature.NestClass$NestClass_1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public void func();
Code:
0: new #2 // class com/janwarlen/feature/NestClass$NestClass_2
3: dup
4: invokespecial #3 // Method com/janwarlen/feature/NestClass$NestClass_2."<init>":()V
7: astore_1
8: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
11: new #5 // class java/lang/StringBuilder
14: dup
15: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
18: ldc #7 // String NestClass_1:func:NestClass_2:name:
20: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: aload_1
24: invokestatic #9 // Method com/janwarlen/feature/NestClass$NestClass_2.access$000:(Lcom/janwarlen/feature/NestClass$NestClass_2;)Ljava/lang/String;
27: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
33: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
36: new #12 // class com/janwarlen/feature/NestClass
39: dup
40: invokespecial #13 // Method com/janwarlen/feature/NestClass."<init>":()V
43: astore_2
44: new #14 // class com/janwarlen/feature/NestClass$InnerClass
47: dup
48: aload_2
49: dup
50: invokevirtual #15 // Method java/lang/Object.getClass:()Ljava/lang/Class;
53: pop
54: invokespecial #16 // Method com/janwarlen/feature/NestClass$InnerClass."<init>":(Lcom/janwarlen/feature/NestClass;)V
57: astore_3
58: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
61: new #5 // class java/lang/StringBuilder
64: dup
65: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
68: ldc #17 // String NestClass_1:func:InnerClass:name:
70: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
73: aload_3
74: invokestatic #18 // Method com/janwarlen/feature/NestClass$InnerClass.access$100:(Lcom/janwarlen/feature/NestClass$InnerClass;)Ljava/lang/String;
77: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
80: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
83: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
86: return
}
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass\$NestClass_2.class
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass$NestClass_2 {
public com.janwarlen.feature.NestClass$NestClass_2();
Code:
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #3 // String NestClass_2
7: putfield #1 // Field name:Ljava/lang/String;
10: return

static java.lang.String access$000(com.janwarlen.feature.NestClass$NestClass_2);
Code:
0: aload_0
1: getfield #1 // Field name:Ljava/lang/String;
4: areturn
}
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass\$InnerClass.class
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass$InnerClass {
final com.janwarlen.feature.NestClass this$0;

public com.janwarlen.feature.NestClass$InnerClass(com.janwarlen.feature.NestClass);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field this$0:Lcom/janwarlen/feature/NestClass;
5: aload_0
6: invokespecial #3 // Method java/lang/Object."<init>":()V
9: aload_0
10: ldc #4 // String InnerClass
12: putfield #1 // Field name:Ljava/lang/String;
15: return

static java.lang.String access$100(com.janwarlen.feature.NestClass$InnerClass);
Code:
0: aload_0
1: getfield #1 // Field name:Ljava/lang/String;
4: areturn
}

JDK11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass.class 
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass {
public com.janwarlen.feature.NestClass();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass\$NestClass_1.class
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass$NestClass_1 {
public com.janwarlen.feature.NestClass$NestClass_1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return

public void func();
Code:
0: new #2 // class com/janwarlen/feature/NestClass$NestClass_2
3: dup
4: invokespecial #3 // Method com/janwarlen/feature/NestClass$NestClass_2."<init>":()V
7: astore_1
8: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_1
12: getfield #5 // Field com/janwarlen/feature/NestClass$NestClass_2.name:Ljava/lang/String;
15: invokedynamic #6, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
20: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: new #8 // class com/janwarlen/feature/NestClass
26: dup
27: invokespecial #9 // Method com/janwarlen/feature/NestClass."<init>":()V
30: astore_2
31: new #10 // class com/janwarlen/feature/NestClass$InnerClass
34: dup
35: aload_2
36: dup
37: invokestatic #11 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
40: pop
41: invokespecial #12 // Method com/janwarlen/feature/NestClass$InnerClass."<init>":(Lcom/janwarlen/feature/NestClass;)V
44: astore_3
45: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
48: aload_3
49: getfield #13 // Field com/janwarlen/feature/NestClass$InnerClass.name:Ljava/lang/String;
52: invokedynamic #14, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
57: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
60: return
}
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass\$NestClass_2.class
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass$NestClass_2 {
public com.janwarlen.feature.NestClass$NestClass_2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String NestClass_2
7: putfield #3 // Field name:Ljava/lang/String;
10: return
}
jan@JanWarlendeMacBook-Pro feature % javap -c NestClass\$InnerClass.class
Compiled from "NestClass.java"
public class com.janwarlen.feature.NestClass$InnerClass {
final com.janwarlen.feature.NestClass this$0;

public com.janwarlen.feature.NestClass$InnerClass(com.janwarlen.feature.NestClass);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lcom/janwarlen/feature/NestClass;
5: aload_0
6: invokespecial #2 // Method java/lang/Object."<init>":()V
9: aload_0
10: ldc #3 // String InnerClass
12: putfield #4 // Field name:Ljava/lang/String;
15: return
}

对比解读

EnclosingMethod

1
2
3
4
5
6
7
8
9
10
11
public com.janwarlen.feature.NestClass$InnerClass(com.janwarlen.feature.NestClass);
Code:
0: aload_0
1: aload_1
2: putfield #2 // Field this$0:Lcom/janwarlen/feature/NestClass;
5: aload_0
6: invokespecial #3 // Method java/lang/Object."<init>":()V
9: aload_0
10: ldc #4 // String InnerClass
12: putfield #1 // Field name:Ljava/lang/String;
15: return

JDK8和JDK11反编译结果中均有此代码,但是源码中只有一个属性,因此我推测这个就是所谓的EnclosingMethod(笔者此时还不了解闭包),而内部类的特殊创建方式估计也是与此有关。需要注意的是Nest Class是没有该代码的。

1
2
NestClass nestClass = new NestClass();
InnerClass i1 = nestClass.new InnerClass();

accessibility-broadening bridge methods

因为在nest class NestClass_1中我们使用了inner class的私有属性,因此,在反编译的结果中,我们看到了JDK8和JDK11的不同之处

1
2
3
4
5
static java.lang.String access$100(com.janwarlen.feature.NestClass$InnerClass);
Code:
0: aload_0
1: getfield #1 // Field name:Ljava/lang/String;
4: areturn

上述汇编代码在JDK11的反编译结果中不存在,需要明白这个编码的作用我们需要再看调用方的反编译汇编码。

调用源码
1
System.out.println("NestClass_1:func:InnerClass:name:" + i1.name);
JDK8调用汇编码
1
2
3
4
5
6
7
8
9
10
11
12
// 部分节选
58: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
61: new #5 // class java/lang/StringBuilder
64: dup
65: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
68: ldc #17 // String NestClass_1:func:InnerClass:name:
70: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
73: aload_3
74: invokestatic #18 // Method com/janwarlen/feature/NestClass$InnerClass.access$100:(Lcom/janwarlen/feature/NestClass$InnerClass;)Ljava/lang/String;
77: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
80: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
83: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
JDK11调用汇编码
1
2
3
4
5
45: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
48: aload_3
49: getfield #13 // Field com/janwarlen/feature/NestClass$InnerClass.name:Ljava/lang/String;
52: invokedynamic #14, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
57: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

accessibility-broadening bridge methods

观察上方两者汇编码,我们可以看出在JDK11中字符串的拼接也进行了优化,此处先不赘述,我们单独看变量使用,JDK8是通过Method com/janwarlen/feature/NestClass$InnerClass.access$100去获得私有属性的值,而JDK11则是直接去获取了属性,这样,突然就明白了为什么说是bridge methods了,并且这样对比,发现汇编的指令码也少了很多,这样对于JVM的性能提升也是有帮助的。

挖坑

期待后续有机会回填,以下的优化点实在是难以通过代码直观查看,因此只好暂时先跳过,也许以后实力见长,可以去深入JVM内部去查看对比,届时再返回填土….

  1. JVM Access Control for Nestmates
  2. Nest Membership Validation

常量池添加新类型CONSTANT_Dynamic

官方的JEP原文看的头痛欲裂,和上一个一样,概念性居多,整篇看下来感觉添加这个是因为本次JDK更新的一些内容和指令invokedynamic经常起冲突导致工作量增多而进行的调整,也算是为后续的工作减少工作量improving program performance and simplifying compiler logic
浏览new-jvm-features-jdk-11hands-on-java-constantdynamic之后,感觉java agent领域的同学可能需要更多的关注这个,可能需要深入研究,尤其是hands-on-java-constantdynamic中有这么一段话(虽然目前并不能想到使用场景以及使用方式)

The constantdynamic feature can also be useful to Java agents that often need to enhance existing classes with additional information. Java agents cannot normally alter a classes by for example adding static fields as this can both interfere with reflection-based frameworks and since class format changes are forbidden on most JVMs when redefining an already loaded class. Neither restriction does however apply to dynamic constants that are added during runtime where a Java agent can now easily tag classes with additional information.

针对AArch64处理器优化部分函数

该特性是硬件适配内容,如果使用的服务器有涉及AArch64处理器,建议增加额外的性能测试,避免未知问题,因为原文中有如下一段话,大致意思就是官方性能测试并未覆盖所有AArch64架构,相当一部分还是依靠OpenJDK社区反馈,因此需要额外注意。

It is not possible to perform testing and performance measurements on all AArch64 hardware variants. We will rely on the OpenJDK Community to perform testing on hardware we currently do not have in-house should they find it necessary when patches are submitted for review.

新GC:Epsilon GC(实验性)

Develop a GC that handles memory allocation but does not implement any actual memory reclamation mechanism. Once the available Java heap is exhausted, the JVM will shut down.

官方文档原文明确指出该GC只处理内存分配,不进行任何内存回收动作(那还叫啥GC…),并且一旦堆内存耗尽,JVM将GG。
使用场景官方文档给出了几个用途:

  1. 做其他GC的性能测试背景板(对比)
  2. 内存压力测试,应该是一些特殊应用可以用到,测试出内存使用的上限,方便启动参数进行内存分配
  3. 声明周期极短的应用,一些应用可以通过进程的消亡快速的释放资源,该场景下GC的内存回收则显得多此一举(不确定理解的是否正确)

删除 Java EE 和 CORBA 模块

JDK9中声明过期的模块,本次进行了大范围的删除。如果有相关代码,版本迁移需要额外注意。

对 HTTP 客户端 API 进行标准化

有部分类和方法存在删除,版本迁移需要额外注意。

Lambda 参数的局部变量语法

1
2
(var x, var y) -> x.process(y)
(x, y) -> x.process(y)

em…可能是为了补充之前lambda不用声明类型的”漏洞”?不使用var也没有什么问题,使用var还必须都使用,不能有入参无类型声明或指明类型,以下为错误用法

1
2
(var x, y) -> x.process(y)         // Cannot mix 'var' and 'no var' in implicitly typed lambda expression
(var x, int y) -> x.process(y) // Cannot mix 'var' and manifest types in explicitly typed lambda expression

拓展两个加密算法实现(Curve25519 和 Curve448)

笔者未涉足过加密算法领域,不太清楚这两个拓展的具体含义,但是根据官方的文档和其他博主的分享,应该是更安全更高效的算法。
官方示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
// alternatively: kpg = KeyPairGenerator.getInstance("X25519")
KeyPair kp = kpg.generateKeyPair();

KeyFactory kf = KeyFactory.getInstance("XDH");
BigInteger u = ...
XECPublicKeySpec pubSpec = new XECPublicKeySpec(paramSpec, u);
PublicKey pubKey = kf.generatePublic(pubSpec);

KeyAgreement ka = KeyAgreement.getInstance("XDH");
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();

Unicode 10持续更新

支持到JDK11发布时的Unicode 10,下列四个未涉及:

  1. UTS #10,Unicode 排序算法
  2. UTS #39,Unicode 安全机制
  3. UTS #46,Unicode IDNA 兼容性处理
  4. UTS #51,Unicode 表情符号

Flight Recorder(飞行记录仪)

好用的性能分析工具,官方说不会超过1%的性能消耗,看起来是可以在生产环境中使用的,但实际还是慎重,除非特殊情况,临时需要该工具进行问题分析,否则不建议常驻。-XX:StartFlightRecording启动参数开启。
读取jmr文件工具:jmc

拓展新的加密算法实现(ChaCha20 和 Poly1305)

这两个加密算法已经是广泛使用,JDK11的该更新算是”紧跟潮流”。

启动单文件源代码程序

看起来和JShell一样,降低部分场景下的Java使用成本。除了一些特殊的小型应用使用场景,不建议使用该功能,哪怕是在学习Java的前期。

低开销堆内存分配管理

对于堆内存管理的一个优化方案,甚至可以查看Java对象的存活信息。

传输层安全性 (TLS)升至1.3

对之前的版本如1.2存在兼容性风险,建议全局统一升级。

新GC:ZGC(实验性)

  1. GC停顿时间优化效果明显
  2. GC停顿时间不会因堆大小有明显变化
  3. JDK11不可以在生产环境中使用

弃用 Nashorn JavaScript 引擎

未删除,仅通过@Deprecated(forRemoval=true)标记,未来版本可能会删除。

弃用 Pack200 工具和 API

和Java应用程序打包有关,属于压缩工具。一句话总结就是跟不上时代的脚步而被抛弃,JDK11仅标记@Deprecated(forRemoval=true)

欢迎关注我的其它发布渠道