JDK 13 于 2019 年 9 月 17 日正式发布。
新版本主要包含五个特性
- JEP 350: Dynamic CDS Archives
- JEP 351: ZGC: Uncommit Unused Memory
- JEP 353: Reimplement the Legacy Socket API
- JEP 354: Switch Expressions (Preview)
- JEP 355: Text Blocks (Preview)
Dynamic CDS Archives(动态 CDS 归档)
首先介绍一下 CDS,CDS 全名为 Class Data Sharing,目的是减少Java编程语言应用程序的启动时间,特别是较小的应用程序,并减少内存占用。实现思路是通过JVM调用期间,将共享的归档文件被映射到内存中,从而节省了加载这些类的成本,并允许多个 JVM 进程共享这些类的大部分 JVM 元数据。
JVM 中 CDS 的历史
- JDK 1.5 引入了CDS 的概念,通过把 rt.jar 中的核心类提前转化成内部表示,转储到一个共享存档(shared archive)中。每个 JVM 只需要装载自己的应用类,启动时间减少了,另外核心类是共享的,所以 JVM 的内存占用也减少了;
- Oracle JDK 9 中,在支持其他 GC 算法和应用程序类的情况下,这变得非常有用;
- JDK 10 之前,它一直是一个商业特性,在JDK 10 中它成为了开源的;
- OpenJDK 13 / JDK 13 中实现了动态 CDS 归档,提高应用程序类数据共享(AppCDS)的可用性。消除了用户为每个应用程序创建类列表而进行试运行的需要。当程序存在时,可以使用 -XX:ArchiveClassesAtExit=<filename>,而不是提供类列表。然后使用 -XX:SharedArchiveFile=<filename>,可以在 JDK 的系统默认存档之上共享类数据
ZGC: Uncommit Unused Memory
ZGC 是在 JDK 11 中引入的,但是到目前为止它还没有像 G1 垃圾收集器那样将未使用的堆内存返回到操作系统。这个 JEP 解决了这个问题,并且默认启用了这个功能。
Reimplement the Legacy Socket API
java.net.Socket 和 java.net.ServerSocket 的实现非常古老,这个 JEP 为它们引入了新的实现。新的实现方法是 Java 13 中的默认实现,但是旧的实现还没有被删除,可以通过设置系统属性 jdk.net.usePlainSocketImpl 来使用它们。但没有为 java.net.DatagramSocket 引入新的实现。
运行一个实例化 Socket 和 ServerSocket 的类将显示这个调试输出。这是默认的(新的实现):
java -XX:+TraceClassLoading JEP353 | grep Socket
[0.033s][info ][class,load] java.net.Socket source: jrt:/java.base
[0.035s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.035s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
[0.042s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.042s][info ][class,load] sun.nio.ch.NioSocketImpl source: jrt:/java.base
[0.043s][info ][class,load] sun.nio.ch.SocketDispatcher source: jrt:/java.base
[0.044s][info ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
[0.044s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.045s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.045s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
sun.nio.ch 上面的 NioSocketImpl 是新的实现。
现在让我们设置系统属性并再次运行:
$ java -Djdk.net.usePlainSocketImpl -XX:+TraceClassLoading JEP353 | grep Socket
[0.037s][info ][class,load] java.net.Socket source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketOptions source: jrt:/java.base
[0.039s][info ][class,load] java.net.SocketImpl source: jrt:/java.base
[0.043s][info ][class,load] java.net.SocketImpl$$Lambda$1/0x0000000800b50840 source: java.net.SocketImpl
[0.046s][info ][class,load] sun.net.PlatformSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.AbstractPlainSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.PlainSocketImpl source: jrt:/java.base
[0.047s][info ][class,load] java.net.AbstractPlainSocketImpl$1 source: jrt:/java.base
[0.047s][info ][class,load] sun.net.ext.ExtendedSocketOptions source: jrt:/java.base
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions source: jrt:/jdk.net
[0.047s][info ][class,load] java.net.SocketOption source: jrt:/java.base
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$ExtSocketOption source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.SocketFlow source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions source: jrt:/jdk.net
[0.047s][info ][class,load] jdk.net.ExtendedSocketOptions$PlatformSocketOptions$1 source: jrt:/jdk.net
[0.048s][info ][class,load] jdk.net.LinuxSocketOptions source: jrt:/jdk.net
[0.048s][info ][class,load] jdk.net.LinuxSocketOptions$$Lambda$2/0x0000000800b51040 source: jdk.net.LinuxSocketOptions
[0.049s][info ][class,load] jdk.net.ExtendedSocketOptions$1 source: jrt:/jdk.net
[0.049s][info ][class,load] java.net.StandardSocketOptions source: jrt:/java.base
[0.049s][info ][class,load] java.net.StandardSocketOptions$StdSocketOption source: jrt:/java.base
[0.051s][info ][class,load] sun.net.ext.ExtendedSocketOptions$$Lambda$3/0x0000000800b51440 source: sun.net.ext.ExtendedSocketOptions
[0.057s][info ][class,load] java.net.DelegatingSocketImpl source: jrt:/java.base
[0.057s][info ][class,load] java.net.SocksSocketImpl source: jrt:/java.base
[0.058s][info ][class,load] java.net.ServerSocket source: jrt:/java.base
[0.058s][info ][class,load] jdk.internal.access.JavaNetSocketAccess source: jrt:/java.base
[0.058s][info ][class,load] java.net.ServerSocket$1 source: jrt:/java.base
它显示了现在使用的旧的实现 java.net.PlainSocketImpl。
Switch Expressions (Preview)
在 JDK 12 中引入了 Switch 表达式作为预览特性。JEP 354 修改了这个特性,它引入了 yield 语句来从块中返回值,而不是使用 break。这意味着,switch 表达式(返回值)应该使用 yield,而 switch 语句(不返回值)应该使用 break。
JDK 13 之前的 Switch 的写法:
int i;
switch (x) {
case "1":
i = 1;
break;
case "2":
i = 2;
break;
default:
i = x.length();
break;
}
JDK 13 的 Switch 的写法:
switch (x) {
case "1" -> 1;
case "2" -> 2;
default -> {
int len = x.length();
yield len;
}
}
或者
switch (x) {
case "1": yield 1;
case "2": yield 2;
default: {
int len = x.length();
yield len;
}
}
Text Blocks (Preview)
JDK 12 中引入了 Raw String Literals 特性,但在发布之前就放弃了。JEP 在引入多行字符串文字(一个文本块)方面与此类似。
HTML 例子:
使用字符串文字:
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world</p>\n" +
" </body>\n" +
"</html>\n";
使用文本块:
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
SQL 例子
使用字符串文字:
String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
"WHERE `CITY` = 'INDIANAPOLIS'\n" +
"ORDER BY `EMP_ID`, `LAST_NAME`;\n";
使用文本块:
String query = """
SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
WHERE `CITY` = 'INDIANAPOLIS'
ORDER BY `EMP_ID`, `LAST_NAME`;
""";
总结:
以上,就是 JDK 13 中包含的 5 个新特性,能够改变开发者编码风格的主要有 Text Blocks 和 Switch Expressions 两个新特性,但是这两个特性还处于预览阶段。
而且,JDK 13 并不是 LTS(长期支持)版本,如果你正在使用 Java 8(LTS)或者 Java 11(LTS),暂时可以不必升级到 Java 13。
想了解更多技术文章,请关注我的公众号:TomScript,获得独家整理的学习资源和日常干货推送。