关于 Java 18 你想知道的一切(上)

简介: 关于 Java 18 你想知道的一切(上)

Java 18 于今天(2022-3-22)发布 GA 版本了,今天也是我和我宝宝领证一周年的日子,为了纪念今天,特此奉上 - 关于 Java 18 你想知道的一切


正式发布的新特性


简易 HTTP 服务器

相关 JEP:


为了方便大家快速建立一个 HTTP 服务器来挂载一些静态文件,实现快速简易测试,演示某些功能,JDK 18 附带了一个简易的 HTTP 服务器 - 在 bin 目录下多了一个工具 jwebserver

可以通过下面的命令行来启动一个简易的 HTTP 服务器:


image.png


可以指定的参数包括:

  • -b addr or --bind-address addr:指定绑定地址,默认 addr 是:127.0.0.1 or ::1 (loopback)
  • -d dir or --directory dir:指定挂载目录,默认 dir 是当前目录,挂载后可以获取文件夹内的内容
  • -o level or --output level:指定日志级别,默认 level 是 info(可以是:none | info | verbose)
  • -p port or --port port:指定端口,默认 port 是 8000

访问可以看到这个相当于是挂载目录的简单文件服务器:


image.png


同时也能在启动的 console 中看到请求的 accesslog:

127.0.0.1 - - [March 21st, 2022:14:25:48 +0800] "GET / HTTP/1.1" 200 -

它只服务于 HEAD 和 GET 请求,不支持身份验证、访问控制、加密等。

你可以通过使用 com.sun.net.httpserver 下的类,自定义这个 HTTP 服务器的配置,自定义 HttpHandler,Filter 这些,例如:


image.png


互联网地址解析 SPI


相关 JEP:


原来 Java 中的互联网地址解析是内置的解析器,即使用本地 'hosts' 文件和 DNS 的组合;Java 18 之后,为互联网地址解析定义了 SPI,这样,'java.net.InetAddress' 可以使用除内置的解析器之外的解析器。

这个主要是为了:

  • 为 Project Loom 做准备:'java.net.InetAddress' 的解析操作目前在操作系统调用中阻塞。这对于 Loom 的虚拟线程来说是个问题,因为这也会阻塞虚拟线程使得调度器无法切换到另一个虚拟线程。通过抽象这个为 SPI 来提供另一个解析器实现非阻塞的 DNS 解析。
  • 兼容新的网络协议:可以实现新的解析协议的无缝集成,比如 DNS over QUIC/TLS/HTTPS。
  • 定制改造解析结果:使框架和应用程序能够更好地控制解析结果,并允许现有的库使用自定义解析器进行改造。
  • 更好的测试:比如你可以实现自己的 SPI 模拟远程请求实际解析到本地的某些地址等等。

这个 SPI 究竟是哪个类,可以参考 java.util.ServiceLoader 的使用,通过里面的 api 指定如下 SPI 接口的实现:java.net.spi.InetAddressResolverProvider


Finalization 的 Deprecate For Removal


相关 JEP:


Java finalization 是 Java 一开始就有的特性,当初设计出来的时候是为了让我们避免资源泄漏:当没有人引用保存资源的实例时然后执行一段代码来回收资源。本着这个思路,就会联想到垃圾回收器知道什么时候是要回收一个对象,所以就利用垃圾回收的机制来执行这段代码就好了。所以,设计出 Object 的 finalize() 方法,Java 类可以覆盖这个方法,在里面填写关闭资源的代码。这段代码会在对象被回收的某个时候被调用。但是这种机制带来了如下几个问题:

  • 假设你的 JVM 老年代增长的很慢,如果你的需要 finalize 的对象进入了老年代,那么可能很久对象都不会被回收。
  • 假设你的需要 finalize 的对象突然增多,创建这种对象的速度要快于 GC 进行收集以及执行 finalize() 方法的速度,这样会造成雪崩
  • 由于无法确定哪个线程执行 finalize() 方法,按照什么顺序执行这些 finalize() 方法,因此在这个方法中不能有影响线程安全的代码,以及乱引用外部对象导致对象又“复活”了

并且,这种 Finalization 还是一个历史包袱,所有的垃圾回收器代码都要不断维护这个执行这些 finalize() 方法的机制,影响了这些垃圾回收器的迭代,并且由于 Finalization 的存在导致 GC 要占用的内存页增加了,ZGC 估计 1.5% 的内存占用只是为了 Finalization 用的。

所以,其实从 Java 9 开始就标记 Object 的 finalize() 方法为 Deprecated 了,现在从 Java 18 开始,正式标记为 Deprecated for removal,也就是不久的将来,这个方法会被完全去掉。


如何验证移除 Finalization 对你的项目是否有影响?

如果你使用了 JFR,可以通过 Java 18 后加入的 JFR 事件 jdk.FinalizerStatistics,来看出你的 JVM 中是否有 Finalization

如果你没有开启 JFR,那么我推荐你使用下 JFR,很好用,参考:JFR 全解

如果你不想通过 JFR,那么你可以先在你的程序运行的时候,记录下:

  • JVM 内存使用情况,建议开启 Native Memory Tracking,参考:JVM相关 - 深入理解 System.gc()
  • 进程相关的文件描述符数量
  • Direct Buffer 以及 MMAP Buffer 使用量:可以通过 JMX 的 MBean 查看,例如:


image.png


记录好之后,启动参数加上 --finalization=disabled,这个参数让所有的 Finalization 机制失效,对比下内存用量,判断是否依赖了 Finalization。


默认编码为 UTF-8


相关 JEP:


Java 中很多方法都带有字符编码集的参数,例如:

new String(new byte[10]);
new String(new byte[10], Charset.defaultCharset());

如果不传的话,就是使用系统的默认字符集,例如 Linux, MacOS 上面一般是 UTF-8,Windows 上面就不是 UTF-8 了。从 Java 18 开始,默认字符集不再和操作系统有关,就是 UTF-8

如果你的运行操作系统就是 Linux, MacOS,或者你的启动参数本身有 -Dfile.encoding=COMPAT 那么基本对你没有任何影响。

如果你想改回原来那种根据操作系统环境指定默认字符集的方式,可以使用这个启动参数:-Dfile.encoding=COMPAT


通过方法句柄(MethodHandle)重新实现 Java 反射接口


相关 JEP:


在 JDK 18 之前,有三种用于反射操作的 JDK 内部机制:

  • 虚拟机本地方法
  • 动态生成的字节码 stub (Method::invoke, Constructor::newInstance) 和依赖 Unsafe 类的字段访问(Field::get and set):主要在 java.lang.reflect
  • 方法句柄(MethodHandle 类):主要在 java.lang.invoke

每次给 Java 添加一些新的结构特性,例如 Record 这些,都需要同时修改这三个的代码,太费劲了。所以 Java 18 中通过使用java.lang.invoke下的类实现了第二种的这些 API,来减少未来添加新的语言特性所需要的工作量。这也是为了 Project Valhalla 的原生值类型(可以栈上分配,类似于 c 语言的 struct,还有其他语言的 inline class)做准备。


相关文章
|
7月前
|
Java
103.【Java Microbenchmark Harness】(三)
103.【Java Microbenchmark Harness】
33 0
103.【Java Microbenchmark Harness】(三)
|
3月前
|
存储 算法 Java
认识java
认识java
13 0
|
8月前
|
druid Java 数据库
Java BasicDAO的详解
Java BasicDAO的详解
45 0
|
Java
Java一些常见的坑
总是觉得自己Java基础还是不行,需要恶补。今天偶然mark了一本《Java解惑》,其中以端程序的方式罗列了95个即常见又不常见的xian(坑)jing(儿),拿来瞻仰一下。
92 0
1100 校庆(JAVA)
2019 年浙江大学将要庆祝成立 122 周年。为了准备校庆,校友会收集了所有校友的身份证号。现在需要请你编写程序,根据来参加校庆的所有人士的身份证号,统计来了多少校友。
1100 校庆(JAVA)
1101 B是A的多少倍(JAVA)
设一个数 A 的最低 D 位形成的数是 ad​。如果把 ad​ 截下来移到 A 的最高位前面,就形成了一个新的数 B。B 是 A 的多少倍?例如将 12345 的最低 2 位 45 截下来放到 123 的前面,就得到 45123,它约是 12345 的 3.66 倍。
 1101 B是A的多少倍(JAVA)
《On Java》介绍
经典书籍续集,《On Java》介绍
1941 0
《On Java》介绍
|
Java
Java总结 - 封装继承多态
我还是一个没有参加工作的小白,所以这篇文章只是一些自己的理解,如有错误请及时指正 面向对象 java实体类中包含什么呢? 属性,设值器(构造器,get/set方法),eauals()方法和hashcode()方法,目前只能想到这么多,然后规划一下:属性(面向对象部分说),属性(类之间的关系(面.
31310 0
|
Java
爱生活,爱Java
你聪明有人会说你心机重,你靠的是努力有人会说你运气好,你说自己天生乐观有人会说你虚假。 有时候,你明明就是一杯白水,却被人硬生生逼成了满肚子憋屈的碳酸饮料。 人一生要遇见太多人,即使有些话字字诛心,也没必要活在他们的眼神里,只要内心澄明,就永远不用讨好一个不懂你的人。
705 0
|
Java
Java的nanoTime()
java有两个获取和时间相关的秒数方法,一个是广泛使用的 System.currentTimeMillis()   返回的是从一个长整型结果,表示毫秒。 另一个是 System.nanoTime()   返回的是纳秒。
1657 0