JDK11 与 JDK8 特性差异浅谈

政采云技术 发表于 3月以前  | 总阅读数:285 次

1.1 基于嵌套的访问控制

如果你在一个类中嵌套了多个类,各类中可以直接访问彼此的私有成员。

因为 JDK11 开始在 public 、protected 、private 的基础上,JVM 又提供了一种新的访问机制:Nest

我们先来看下 JDK11 之前的如下案例:

class Outer {
  private int outerInt;
  class Inner {
 public void test() {
   System.out.println("Outer int = " + outerInt);
 }
  }
}

在 JDK11 之前 执行编译的最终结果的 class 文件形式如下:

class Outer {
  private int outerInt;
 public int access$000() {
   return outerInt;
  }
}
class Inner$Outer {
  Outer outer;
  public void test() {
    System.out.println("Outer int = " + outer.access$000());
  }
}

以上方案虽然从逻辑上讲,内部类是与外部类相同的代码实体的一部分,但它被编译为一个单独的类。因此,它需要编译器创建合成桥接方法,以提供对外部类的私有字段的访问。

这种方案一个很大的坑是反射的时候会有问题。

当使用反射在内部类中访问外部类的私有成员 outerInt 时会报 IllegalAccessError 错误。这个是让人不能理解的,因为反射还是源码级访问的权限。

class Outer {
  private int outerInt;
  class Inner {
    public void test() throws Exception {
     System.out.println("Outer int = " + outerInt);
      // JDK 11 之前,如下代码报出异常:IllegalAccessException
      Class c = Outer.class ;
      Field f = c.getDeclaredField("outerInt");
      f.set(Outer.this, 23);
    }
  }
  public static void main(String[] args) throws Exception {
    new Outer().new Inner().test();
  }
}

JDK11 开始,嵌套是一种访问控制上下文,它允许多个 class 同属一个逻辑代码块,但是被编译成多个分散的 class 文件,它们访问彼此的私有成员无需通过编译器添加访问扩展方法,而是可以直接进行访问,如果上述代码可以直接通过反射访问外部类的私有成员,而不会出现权限问题,请看如下代码:

class Outer {
  private int outerInt;
  class Inner {
    public void test() throws Exception {
      System.out.println("Outer int = " + outerInt);
      // JDK 11 之后,如下代码不会出现异常
      Class c = Outer.class ;
      Field f = c.getDeclaredField("outerInt");
      f.set(Outer.this, 23);
    }
  }
  public static void main(String[] args) throws Exception {
    new Outer().new Inner().test();
  }
}

1.2 Java 平台级模块系统

Java9 的定义功能是一套全新的模块系统。当代码库越来越大,创建复杂,盘根错节的“意大利面条式代码”的几率呈指数级的增⻓。这时候就得面对两个基础的问题:很难真正地对代码进行封装,而系统并没有对不同部分(也就是 JAR 文件)之间的依赖关系有个明确的概念。每一个公共类都可以被类路径之下任何其它的公共类所访问到,这样就会导致无意中使用了并不想被公开访问的 API 。此外,类路径本身也存在问题:你如何知晓所有需要的 JAR 都已经有了,或者是不是会有重复的项呢?模块系统把这俩个问题都给解决了。

模块化的 JAR 文件都包含一个额外的模块描述器。在这个模块描述器中,对其它模块的依赖是通过 “ requires ” 来表示的。另外“ exports ”语句控制着哪些包是可以被其它模块访问到的。所有不被导出的包默认都封装在模块的里面。

Java 平台本身也使用自己的模块系统进行了模块化。通过封装 JDK 的内部类,平台更安全,持续改进也更容易。

以下是 Java9 平台级模块系统的优势:

  1. 代码内聚,容易维护:模块化可以帮助开发人员在构建、维护和改进软件系统(尤其是大型系统)时提高工作效率。
  2. 降低复杂度:模块化在包之上提供更高层次的聚合,使得代码更易于理解和管理。
  3. 提供更好的伸缩性和扩展性:模块化使得 JavaSE 程序更加容易轻量级部署,改进了组件间的依赖管理。
  4. 改进性能和安全性:模块中的包只有在模块显式导出时可供其他模块访问,即使如此,其他模块也无法使用这些包,除非它明确指出它需要其他模块的功能。由于潜在攻击者只可以访问较少的类别,因此提高了平台的安全性。
  5. 更佳的平台完整性:在 Java9 之前,您可以使用平台中许多不预期供应用使用的类别,通过强封装,这些内部API真正会被封装并隐藏在使用该平台的应用中。
  6. 可扩展的 Java 平台:现在,平台被模块化为 95 个模块(此数字可能随着 Java 的发展而变化),您可以创建定制运行时,其中仅包含应用所需的模块或目标设备。

1.3 JShell(交互式 Java REPL)

JDK9 中的 JShell 让 Java 具有交互式编程环境。现在可以从控制台启动 JShell ,并直接启动输入和执行 Java 代码。JShell 的即时反馈使它成为探索 API 和尝试语言特性的好工具。

jshell> int a = 5;
a ==> 5
jshell> int b = 10;
b ==> 10
jshell> System.out.println("The sum of " + a + " and " + b + " is " + sum);
The sum of 5 and 10 is 15

1.4 根证书颁发认证

根证书是一种特殊的证书,它在公开密钥基础设施( PKI )中,起着信任链的起点的角色。根证书是属于根证书颁发机构( CA )的公钥证书,这个机构的角色有如现实世界中的公证行,保证网络世界中电子证书持有人的身份。

具体来说,根证书是用来签发其他证书的,它自己的签发者就是它本身。许多应用软件(例如操作系统、网页浏览器)会预先安装可被信任的根证书,这代表用户授权了应用软件代为审核哪些根证书机构属于可靠。

OpenJDK 和 OracleJDK 在 JDK9 之前处理根证书方面有所不同,这会影响到它们的构建过程。通过改进根证书的颁发和认证,可以使 OpenJDK 对开发人员更具吸引力,并减少 OpenJDK 和 OracleJDK 构建之间的差异。

1.5 私有接口方法

JDK8 为我们带来了接口的默认方法。接口现在也可以包含行为,而不仅仅是方法签名。但是,如果在接口上有几个默认方法,代码几乎相同,会发生什么情况?通常,您将重构这些方法,调用一个可复用的私有方法。但默认方法不能是私有的。将复用代码创建为一个默认方法不是一个解决方案,因为该辅助方法会成为公共 API 的一部分。使用 JDK9 ,您可以向接口添加私有辅助方法来解决此问题。如果您使用默认方法开发 API ,那么私有接口方法可能有助于构建其实现。

1.6 增强的 try-with-resources

这是 JDK9 中引入的一项新特性,称为 " 增强的 try-with-resources "。在 JDK7 中引入的原始 try-with-resources 语句要求您在 try 语句中声明和初始化一个或多个资源。这些资源在 try 代码块结束时自动关闭。

然而,在 JDK9 中,如果您已经有一个资源是 final 或等效于 final 变量,您可以在 try-with-resources 语句中使用该变量,而无需在 try-with-resources 语句中声明一个新变量。

例如,以下是 JDK7 的 try-with-resources 语句:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  return br.readLine();
}

在 JDK9 中,如果 br 已经被声明并初始化,那么你可以这样写:

BufferedReader br = new BufferedReader(new FileReader(path));
try (br) {
  return br.readLine();
}

在 JDK9 中增强的 try-with-resources 语句中,当 try 代码块执行完毕后,系统会自动调用资源对象的 close() 方法来关闭资源。这就是为什么我们使用 try-with-resources 语句,它可以帮助我们自动管理资源,避免因忘记关闭资源而导致的资源泄露问题。

如果你试图再次从 br 读取数据,将会抛出 IOException

1.7 实施 ChaCha20 和 Poly1305 加密算法

ChaCha20 是一种相对较新的流密码,可以取代旧的、不安全的 R4 流密码。ChaCha20 将与 Poly1305 验证器配对。

ChaCha20 和 Poly1305 加密算法在许多场景中都有应用,特别是在需要数据加密和数据完整性验证的场合。以下是一些具体的应用场景:

  1. 网络通信安全:ChaCha20 和 Poly1305 经常一起使用,形成了被广泛应用的 ChaCha20-Poly1305 加密方案。例如,Google 选择了伯恩斯坦设计的,带 Poly1305 消息认证码的 ChaCha20 ,作为 OpenSSL 中 RC4 的替代品,用以完成互联网的安全通信。Google 最初实现了 HTTPS (TLS/SSL) 流量在 Chrome 浏览器( Android 手机版)与 Google 网站之间的通信。
  2. 软件性能优化:由于 ChaCha20-Poly1305 在没有硬件加速的情况下,通常比 AES-GCM 有更快的软件性能,因此在需要高性能加密的软件中,ChaCha20-Poly1305 是一个很好的选择。
  3. 数据完整性验证:Poly1305 可以用作一次性消息认证码,用于验证单个消息的完整性和真实性,隐藏单个消息的内容。

1.8 局部变量 var 类型推断

在 JDK10 中引入的 var 关键字,允许编译器根据变量的初始赋值来推断出它的类型。这种类型推断可以使代码更简洁,提高代码的可读性。

var rs = "itheima";
System.out.println(rs);
就等于:
String rs = "itheima"

局部变量推断对于较复杂的类型也有很好的简化作用,如下是对Map集合的遍历方式的简化:

public class Demo01 {
  public static void main(String[] args) {
    Map<String,Integer> maps = new HashMap<>();
    // 1.添加元素:添加键值对元素
    maps.put("iphoneX" , 1);
    maps.put("huawei" , 8);
    maps.put("Java" , 1);
    /*
    Set<Map.Entry<String, Integer>> entries = maps.entrySet();
    for(Map.Entry<String,Integer> entry : entries){
      String key = entry.getKey();
      Integer value = entry.getValue();
    }
    */
    var entries = maps.entrySet();
    for(var entry : entries){
      String key = entry.getKey();
      Integer value = entry.getValue();
    }
  }
}

在 JDK11 中,var 关键字可以用于 lambda 表达式的参数。这意味着我们可以在 lambda 表达式的参数上添加注解。例如,我们可以使用 @Deprecated 注解来标记一个参数已经被废弃或不推荐使用。

需要注意的是,如果在lambda表达式中使用了 var 来声明参数,那么所有的参数都必须使用 var 来声明。

List<Integer> nums = new ArrayList<>();
Collections.addAll(nums , 9 , 10 , 3 );
nums.sort((@Deprecated var o2 , @Deprecated var o1) -> o2 - o1);

var 局部变量语法注意事项:

  1. 不可以直接 var a; ,因为无法推断。
  2. 类的属性的数据类型不可以使用 var 。
  3. var 不同于 js ,类型依然是静态类型,var 不可以在 lambda 表达式中混用。
 (var a, b) -> a + b

1.9 String 新增处理方法

如以下所示,JDK11 新增了一些对于 String 的处理方法。

// 判断字符串是否为空白
System.out.println(" ".isBlank()); // true
// 去除首尾空白
System.out.println(" itheima ".strip()); // 可以去除全角的空白字符
System.out.println(" itheima ".trim()); // 不能去除全角的空白字符
// 去除尾部空格
System.out.println(" itheima ".stripTrailing());
// 去除首部空格
System.out.println(" itheima ".stripLeading());
// 复制字符串
System.out.println( "itheima".repeat( 3 )); // itheimaitheimaitheima
// 行数统计
System.out.println("A\nB\nC".lines().count()); // 3 ;

1.10 集合新增的 API

集合( List / Set / Map )都添加了 of 和 copyOf 方法,它们两个都用来创建不可变的集合。

// List的增强api
List<String> list = List.of("aa", "bb", "cc", "dd");
// Set的增强api
Set<Integer> set1 = Set.of(100, 30, 20, 15);
// Map的增强api
Map<String, Integer> map = Map.of("a", 1, "b", 2, "c", 3);

使用 of() 方法创建的集合,为不可变集合,不能进行添加、删除、替换、排序等操作, 不然会报 java.lang.UnsupportedOperationException 异常。

// 把List集合转换成数组
// JDK 11 前的方式
Integer[] nums 1 = list.toArray(new Integer[ 0 ]);
// JDK 11 开始之后新增方式
Integer[] nums 2 = list.toArray(Integer[]::new);
takeWhile:从集合开头提取符合条件的元素
public void test2() {    
  List<Integer> res = Stream.of( 1, 2, 3, 4, 0, 1 )
              .takeWhile( n -> n < 4 )
              .collect( Collectors.toList() );     
}
dropWhile:从集合开头移除前符合条件的元素
public void test3() {    
  List<Integer> res = Stream.of( 1, 2, 3, 4, 0, 1 )
              .dropWhile( n -> n < 4 )
              .collect( Collectors.toList() );    
}

1.11 Optional 新增的 API

Optional.orElseThrow():如果值存在,返回包含的值,否则抛出 NoSuchElementException 。

Optional<String> optional = Optional.of("java");
optional.orElseThrow(); // 返回 "java"

Optional.stream():如果值存在,返回一个包含值的 Stream,否则返回一个空的 Stream 。

Optional<String> optional = Optional.of("java");
optional.stream().count(); // 返回 1

Optional.or(Supplier):如果值存在,返回包含的值,否则返回从指定 Supplier 接口生成的值。

Optional<String> optional = Optional.ofNullable(null);
optional.or(() -> Optional.of("java")).get(); // 返回 "java"

1.12 更方便的 IO

此前我们需要使用 Paths.get() 。现在,我们像其他类一样使用 of() 。

of(String, String...)

writeString(Path, CharSequence) 我们可以使用该方法来保存一个 String 字符串。

Files.writeString(Path.of("test.txt"), "Hello!!!")
readString(Path) 我们可以使用该方法来读取一个 String 字符串。

readString(Path) 我们可以使用该方法来读取一个 String 字符串。

Files.readString(Path.of("test.txt"), StandardCharsets.UTF_8);
  • nullReader() 使用该方法,我们可以得到一个不执行任何操作的 Reader 。
  • nullWriter() 使用该方法,我们可以得到一个不执行任何操作的 Writer 。
  • nullInputStream() 使用该方法,我们可以得到一个不执行任何操作的 InputStream 。
  • nullOutputStream() 使用该方法,我们可以得到一个不执行任何操作的 OutputStream 。
  • InputStream 还终于有了一个非常有用的方法:transferTo ,可以用来将数据直接传输到 OutputStream ,这是在处理原始数据流时非常常见的一种用法,如下示例:
try (var is = Demo01.class.getResourceAsStream("dlei.txt");
  var os = new FileOutputStream("stream01.txt")) {
  is.transferTo(os); // 把输入流中的所有数据直接自动地复制到输出流中
}

1.13 Java HTTP 客户端

从 JDK9 开始引入了一个处理 HTTP 请求的的 HTTP Client API,不过当时一直处于孵化阶段,而在 JDK11 中已经为正式可用状态,作为一个标准API提供在java.net.http供大家使用,该 API 支持同步和异步请求,取代繁琐的 HttpURLConnection 的请求。

以下是 HTTP Client 的用法:

同步请求:

// 1 .创建HttpClient对象。
var client = HttpClient.newHttpClient();
// 2 .创建请求对象:request,封装请求地址和请求方式get.
var request = HttpRequest.newBuilder()
   .uri(URI.create("http://api.com:8899/?appkey=10003&format=json"))
   .GET().build();
// 3 .使用HttpClient对象发起request请求。得到请求响应对象response
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 4 .得到响应的状态码信息
System.out.println(response.statusCode());
// 5 .得到响应的数据信息输出
System.out.println(response.body());

异步请求:

// 1 .创建HttpClient对象。
var client = HttpClient.newHttpClient();
// 2 .创建请求对象:request,封装请求地址和请求方式get.
var request = HttpRequest.newBuilder()
   .uri(URI.create("http://api.com:8899/?appkey=10003&format=json"))
      .GET().build();
// 3 .使用HttpClient对象发起request异步请求。得到请求响应对象future
CompletableFuture<HttpResponse<String>> future =
      client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
// 4 .监听future对象的响应结果,并通过join方法进行异步阻塞式等待。
future.whenComplete((resp,ex) - > {
 if(ex != null ){
   ex.printStackTrace();
  } else{
    System.out.println(resp.statusCode());
    System.out.println(resp.body());
  }
}).join();

1.14 Unicode10

目标:升级现有平台的 API ,支持 Unicode10 ,Unicode10 的标准请参考网站(http://unicode.org/versions/Unicode10.0.0)

目前支持最新的 Unicode 的类主要有

java.lang 包下的 Character、String

java.awt.font 下的相关类

java.text 包下的 Bidi、Normalizer 等

String 对 Unicode 的示例:

System.out.println("\uD83E\uDD93");
System.out.println("\uD83E\uDD92");
System.out.println("\uD83E\uDDDA");
System.out.println("\uD83E\uDDD9");
System.out.println("\uD83E\uDDD1");
System.out.println("\uD83E\uDDD8");
System.out.println("\uD83E\uDD95");
System.out.println("\uD83E\uDD2e");

1.15 改进 Aarch64 函数

改进现有的字符串和数组函数,并在 Aarch64 处理器上为 java.lang.Math 下的 sin 、cos 和 log 函数实现新的内联函数。从而实现专用的 CPU 架构下提高应用程序的性能。

代码示例:

public static void main(String[] args) {
  long startTime = System.nanoTime();
  for(int i = 0 ; i < 10000000 ; i++ ){
    Math.sin(i);
    Math.cos(i);
    Math.log(i);
  }
  long endTime = System.nanoTime();
  // JDK 11 下耗时:1564 ms
  // JDK 8 前耗时:10523 ms
}

1.16 更简化的编译运行程序

增强 Java 启动器支持运行单个 Java 源代码文件的程序。

  • JDK11 中,通过 java xxx.java 命令,就可直接运行源码文件程序,而且不会产生 .class 文件。
  • 一个 Java 文件中包含多个类时,java xxx.java 执行排在最上面的一个类的 main 方法。
  • java xxx.java 启动单个 Java 源代码文件的程序时,相关个类必须定义在同一个 Java 文件中。

JDK11 之前 :

javac Test.java
java Test

JDK11 :

java Test.java

1.17 Epsilon 垃圾收集器

Epsilon :无操作垃圾收集器( Epsilon :A No-Op Garbage Collector )

Epsilon 垃圾收集器主要用途

  1. 性能测试:EpsilonGC 可以帮助开发者理解在没有垃圾收集的情况下,工作负载的运行速度会有多快。
  2. 内存压力测试:使用 EpsilonGC ,开发者可以观察应用程序在内存压力下的表现。
  3. 虚拟机接口测试:EpsilonGC 可以用于测试和验证虚拟机的接口。
  4. 极短生命周期的任务:对于生命周期非常短的程序,使用 EpsilonGC 可以避免在程序结束时进行无用的垃圾收集。
  5. 极致的延迟改进:在某些对延迟特别敏感的情况下,使用 EpsilonGC 可以帮助减少延迟。
  6. 极致的吞吐量改进:在某些对吞吐量特别敏感的情况下,使用 EpsilonGC 可以帮助提高吞吐量。

需要注意的是,EpsilonGC 并不回收任何内存,一旦 Java 堆内存耗尽,JVM 将会关闭。因此,它并不适合用于常规的生产环境。在使用 EpsilonGC 时,需要确保可用的堆内存足够应用程序运行所需。

使用 Epsilon 垃圾收集器

UnlockExperimentalVMOptions :解锁隐藏的虚拟机参数

-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -Xms100m -Xmx100m

如果使用选项 -XX:+UseEpsilonGC ,程序很快就因为堆空间不足而退出。

运行程序后,结果如下:

Terminating due to java.lang.OutofMemoryError: Java heap space

会发现很快就内存溢出了,因为 Epsilon 不会去回收对象。

1.18 ZGC 可伸缩低延迟垃圾收集器

ZGC 全称是 Z Garbage Collector ,ZGC 的设计目标是:支持TB级内存容量,暂停时间低( <10ms ),对整个程序吞吐量的影响小于 15% 。

应用程序同时为数千甚至数百万用户提供服务的情况并不少见。这些应用程序需要大量内存。但是,管理所有内存可能会轻易影响应用程序性能。为了解决这个问题,JDK11 引入了 Z垃圾收集器( ZGC )作为实验性垃圾收集器( GC )实现。

GC术语

为了理解 ZGC 如何匹配现有收集器,以及如何实现新 GC ,我们需要先了解一些术语。最基本的垃圾收集涉及识别不再使用的内存并使其可重用。现代收集器在几个阶段进行这一过程,对于这些阶段我们往往有如下描述:

  • 并行:在 JVM 运行时,同时存在应用程序线程和垃圾收集器线程。并行阶段是由多个 GC 线程执行,即 GC 工作在它们之间分配。不涉及 GC 线程是否需要暂停应用程序线程。
  • 串行:串行阶段仅在单个 GC 线程上执行。与之前一样,它也没有说明 gc 线程是否需要暂停应用程序线程。
  • STW:STW 阶段,应用程序线程被暂停,以便 GC 执行其工作。当应用程序因为 GC 暂停时,这通常是由于 Stop The World 阶段。
  • 并发:如果一个阶段是并发的,那么 GC 线程可以和应用程序线程同时进行。并发阶段很复杂,因为它们需要在阶段完成之前处理可能使工作无效。
  • 增量:如果一个阶段是增量的,那么它可以运行一段时间之后由于某些条件提前终止,例如需要执行更高优先级的 GC 阶段,同时仍然完成生产性工作。增量阶段与需要完全完成的阶段形成鲜明对比。

工作原理

为了实现其目标,ZGC 给 Hotspot Garbage Collectors 增加了两种新技术:着色指针和读屏障。

着色指针

着色指针是一种将信息存储在指针(或使用 Java 术语引用)中的技术。因为在 64 位平台上( ZGC 仅支持 64 位平台),指针可以处理更多的内存,因此可以使用一些位来存储状态。ZGC 将限制最大支持 4Tb 堆( 42-bits ),那么会剩下 22 位可用,它目前使用了 4 位:finalizable 、remap 、mark0 和 mark1 。

着色指针的一个问题是,当您需要取消着色时,它需要额外的工作(因为需要屏蔽信息位)。像 SPARC 这样的平台有内置硬件支持指针屏蔽所以不是问题,而对于 x86 平台来说,ZGC 团队使用了简洁的多重映射技巧。

多重映射

要了解多重映射的工作原理,我们需要简要解释虚拟内存和物理内存之间的区别。物理内存是系统可用的实际内存,通常是安装的 DRAM 芯片的容量。虚拟内存是抽象的,这意味着应用程序对(通常是隔离的)物理内存有自己的视图。操作系统负责维护虚拟内存和物理内存范围之间的映射,它通过使用页表和处理器的内存管理单元( MMU )和转换查找缓冲器( TLB )来实现这一点,后者转换应用程序请求的地址。

多重映射涉及将不同范围的虚拟内存映射到同一物理内存。由于设计中只有一个 remap 、mark0 和 mark1 在任何时间点都可以为 1 ,因此可以使用三个映射来完成此操作。ZGC 源代码中有一个很好的图表可以说明这一点。

读屏障

读屏障是每当应用程序线程从堆加载引用时运行的代码片段( 即访问对象上的非原生字段 non-primitive field ):

void printName( Person person ) {    
  String name = person.name;  // 这里触发读屏障,因为需要从heap读取引用                                  
  System.out.println(name);   // 这里没有直接触发读屏障 
}

在上面的代码中,String name = person.name 访问了堆上的person引用,然后将引用加载到本地的 name 变量。此时触发读屏障。Systemt.out 那行不会直接触发读屏障,因为没有来自堆的引用加载( name 是局部变量,因此没有从堆加载引用 )。但是 System 或者 println 内部可能会触发其他读屏障。

这与其他 GC 使用的写屏障形成对比,例如 G1 。读屏障的工作是检查引用的状态,并在将引用(或者甚至是不同的引用)返回给应用程序之前执行一些工作。在 ZGC 中,它通过测试加载的引用来执行此任务,以查看是否设置了某些位。如果通过了测试,则不执行任何其他工作,如果失败,则在将引用返回给应用程序之前执行某些特定于阶段的任务。

GC循环

标记

GC 循环的第一部分是标记。标记包括查找和标记运行中的应用程序可以访问的所有堆对象,换句话说,查找不是垃圾的对象。

ZGC 的标记分为三个阶段。第一阶段是 STW ,其中 GC roots 被标记为活对象。GC roots 类似于局部变量,通过它可以访问堆上其他对象。如果一个对象不能通过遍历从 roots 开始的对象图来访问,那么应用程序也就无法访问它,则该对象被认为是垃圾。从 roots 访问的对象集合称为 Live 集。GC roots 标记步骤非常短,因为 roots 的总数通常比较小。

该阶段完成后,应用程序恢复执行,ZGC 开始下一阶段,该阶段同时遍历对象图并标记所有可访问的对象。在此阶段期间,读屏障针使用掩码测试所有已加载的引用,该掩码确定它们是否已标记或尚未标记,如果尚未标记引用,则将其添加到队列以进行标记。

在遍历完成之后,有一个最终的、时间很短的 Stop The World 阶段,这个阶段处理一些边缘情况(我们现在将它忽略),该阶段完成之后标记阶段就完成了。

重定位

GC 循环的下一个主要部分是重定位。重定位涉及移动活动对象以释放部分堆内存。为什么要移动对象而不是填补空隙?有些 GC 实际是这样做的,但是它导致了一个不幸的后果,即分配内存变得更加昂贵,因为当需要分配内存时,内存分配器需要找到可以放置对象的空闲空间。相比之下,如果可以释放大块内存,那么分配内存就很简单,只需要将指针递增新对象所需的内存大小即可。

ZGC 将堆分成许多页面,在此阶段开始时,它同时选择一组需要重定位活动对象的页面。选择重定位集后,会出现一个 Stop The World 暂停,其中 ZGC 重定位该集合中 root 对象,并将他们的引用映射到新位置。与之前的 Stop The World 步骤一样,此处涉及的暂停时间仅取决于 root 的数量以及重定位集的大小与对象的总活动集的比率,这通常相当小。所以不像很多收集器那样,暂停时间随堆增加而增加。

移动 root 后,下一阶段是并发重定位。在此阶段,GC 线程遍历重定位集并重新定位其包含的页中所有对象。如果应用程序线程试图在 GC 重新定位对象之前加载它们,那么应用程序线程也可以重定位该对象,这可以通过读屏障(在从堆加载引用时触发)实现,如流程图如下所示:

这可确保应用程序看到的所有引用都已更新,并且应用程序不可能同时对重定位的对象进行操作。

GC 线程最终将对重定位集中的所有对象重定位,然而可能仍有引用指向这些对象的旧位置。GC 可以遍历对象图并重新映射这些引用到新位置,但是这一步代价很高昂。因此这一步与下一个标记阶段合并在一起。在下一个 GC 周期的标记阶段遍历对象对象图的时候,如果发现未重映射的引用,则将其重新映射,然后标记为活动状态。

用法

运行我们的应用程序时,我们可以使用以下命令行选项启用 ZGC :

XX:+UnlockExperimentalVMOptions - XX:+UseZGC

注意:因为ZGC还处于实验阶段,所以需要通过 JVM 参数 UnlockExperimentalVMOptions 来解锁这个特性。

平台支持

ZGC目前只支持 64 位的linux系统。

ZGC和G1停顿时间比较

ZGC

avg: 1.021 ms (+/- 0.215 ms)
95th percentile: 1.392 ms
99th percentile: 1.512 ms
99.9th percentile: 1.663 ms
99.99th percentile: 1.681 ms
max: 1.681 ms

G1

avg: 157.202 ms (+/- 71.126 ms)
95th percentile: 316.672 ms
99th percentile: 428.095 ms
99.9th percentile: 543.846 ms
99.99th percentile: 543.846 ms
max: 543.846 ms

Stefan Karlsson 和 Per Liden 在早些时候的 Jfokus 演讲中给出了一些数字。ZGC 的 SPECjbb2015 吞吐量与 Parallel GC (优化吞吐量)大致相当,但平均暂停时间为 1ms ,最长为 4ms 。与之相比 G1 和 Parallel 有很多次超过 200ms 的 GC 停顿。

1.19 其他新特性

移除项
  • 1 、移除了com.sun.awt.AWTUtilities
  • 2 、移除了sun.misc.Unsafe.defineClass,使用java.lang.invoke.MethodHandles.Lookup.defineClass来替
  • 3 、移除了Thread.destroy()以及 Thread.stop(Throwable)方法
  • 4 、移除了sun.nio.ch.disableSystemWideOverlappingFileLockCheck、sun.locale.formatasdefault属性
  • 5 、移除了jdk.snmp模块
  • 6 、移除了javafx,openjdk估计是从java 10 版本就移除了,oracle jdk 10 还尚未移除javafx,而java 11 版本则oracle的jdk版本也移除了javafx
  • 7 、移除了Java Mission Control,从JDK中移除之后,需要自己单独下载
  • 8 、移除了这些Root Certificates:Baltimore Cybertrust Code Signing CA,SECOM ,AOL and Swisscom
废弃项
  • 1 、-XX:+AggressiveOpts选项
  • 2 、-XX:+UnlockCommercialFeatures
  • 3 、-XX:+LogCommercialFeatures选项也不再需要
JEP320:移除Java EE和CORBA模块(Remove the Java EE and CORBA Modules)
  • 1 、java.xml.ws,
  • 2 、java.xml.bind,
  • 3 、java.xml.ws,
  • 4 、java.xml.ws.annotation,
  • 5 、jdk.xml.bind,
  • 6 、jdk.xml.ws被移除,
  • 只剩下java.xml,java.xml.crypto,jdk.xml.dom这几个模块
  • 7 、java.corba,
  • 8 、java.se.ee,
  • 9 、java.activation,
  • 10 、java.transaction被移除,但是java 11 新增一个java.transaction.xa模块
JEP335:Deprecate the Nashorn JavaScript Engine

废除Nashorn javascript引擎,在后续版本准备移除掉。

JEP336:Deprecate the Pack200 Tools and API

JDK5 中带了一个压缩工具:Pack200,在 JDK11 中废除了 pack200 以及 unpack200 工具以及 java.util.jar 中的 Pack200 API 。因为 Pack200 主要是用来压缩 jar 包的工具,由于网络下载速度的提升以及 JDK9 引入模块化系统之后不再依赖 Pack200 ,因此这个版本将其移除掉。

JEP332:Transport Layer Security (TLS) 1.3

实现 TLS 协议 1.3 版本。( TLS 允许客户端和服务端通过互联网以一种防止窃听,篡改以及消息伪造的方式进行通信 )。

TLS1.3 是 TLS 协议的重大改进,与以前的版本相比,它提供了显着的安全性和性能改进。其他供应商的几个早期实现已经可用。我们需要支持 TLS1.3 以保持竞争力并与最新标准保持同步。这个特性的实现动机和 Unicode10 一样,也是紧跟历史潮流。

JEP328:Flight Recorder

排错、监控、性能分析是整个开发生命周期必不可少的一部分,但是某些问题只会在大量真实数据压力下才会发生在生产环境。

Flight Recorder 记录源自应用程序,JVM 和 OS 的事件。事件存储在一个文件中,该文件可以附加到错误报告中并由支持工程师进行检查,允许事后分析导致问题的时期内的问题。工具可以使用 API 从记录文件中提取信息。

1.20 总结

从 JDK8 到 JDK11 到 JDK17 再到目前的 JDK21 ,Java 的发展趋势显示出对性能优化、编程便利性和功能增强的持续关注。引入的新特性,如变量类型推断、官方 HTTP Client 和 String 处理增强,都是为了提高开发者的编程效率。同时,内部升级如更低的 GC 开销和时延,以及对 TLS1.3 的支持,都是为了提高 Java 应用的运行效率和安全性。这些改进和新特性的引入,都反映出 Java 在持续适应和引领现代编程需求的趋势。

1.21 参考

  1. Oracle JDK 官方文档
  2. Open JDK 官方文档
  3. JAVA 8与JAVA 11到底该怎么选?- Rooker - 博客园
  4. JDK8 和 JDK11 Java 两个主要版本的比较 - CSDN博客
  5. Java中JDK8、JDK11、JDK17,该怎么选择?- 阿里云开发者社区

本文由微信公众号政采云技术原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/Ki2qZxQY7xG86EzrTsohiA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:8月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:8月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:8月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:8月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:8月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:8月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:8月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:8月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:8月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:8月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:8月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:8月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:8月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:8月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:8月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:8月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:8月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:8月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:8月以前  |  398次阅读  |  详细内容 »
 目录