一、导语

几天前Oracle刚刚发布了Java21,

由于这是最新的LTS版本,引起了大家的关注。

我也第一时间在个人项目中进行了升级体验。

一探究竟,和大家分享。

二、Java21更新内容介绍

官方release公告:

https://jdk.java.net/21/release-notes

开源中国介绍:

https://my.oschina.net/waylau/blog/10112170

新特性一览:

  • JEP 431:序列集合

  • JEP 439:分代 ZGC

  • JEP 440:记录模式

  • JEP 441:switch 模式匹配

  • JEP 444:虚拟线程

  • JEP 449:弃用 Windows 32 位 x86 移植

  • JEP 451:准备禁止动态加载代理

  • JEP 452:密钥封装机制 API

  • JEP 430:字符串模板(预览)

  • JEP 442:外部函数和内存 API(第三次预览)

  • JEP 443:未命名模式和变量(预览)

  • JEP 445:未命名类和实例主方法(预览)

  • JEP 446:作用域值(预览)

  • JEP 453:结构化并发(预览)

  • JEP 448:Vector API(孵化器第六阶段)

其中大家比较关注的是分代 ZGC和虚拟线程。

三、开箱

下载地址:

OpenJDK 版本:https://jdk.java.net/21/

Oracle 版本:https://www.oracle.com/java/technologies/downloads/

对比17

边框由不锈钢升级为钛金属

目录结构一致:

模块数量比17少一个:

整体大小从289MB增加到了320MB

四、升级体验

下载

更新pom

尝试运行

运行报错:

java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field 'com.sun.tools.javac.tree.JCTree qualid'

解决办法:

升级lombok至1.18.30

原因:https://github.com/projectlombok/lombok/issues/3393

兼容性检查:

由于我的项目以前用的JDK17,本次升级兼容性良好,只发现了一处:

系统托盘中 使用了PopupMenu,出现了字符集问题:

五、分代ZGC体验

ZGC在之前的JDK版本中也有,这次的分代ZGC更是被大家看好,官方的介绍如下:

Applications running with Generational ZGC should enjoy:

Lower risks of allocations stalls,

Lower required heap memory overhead, and

Lower garbage collection CPU overhead.

Enable Generational ZGC with command line options -XX:+UseZGC -XX:+ZGenerational

性能测试参考:

https://inside.java/2023/09/03/roadto21-performance/

JVM参数:-XX:+UseZGC -XX:+ZGenerational

使用Java21,未使用ZGC

MooInfo内存占用查看



使用Java21,使用分代ZGC

MooInfo内存占用查看





以上只是初步体验,关于ZGC的更多内容,如详细的分代回收情况后续进一步探索。

以上内存占用查看使用我之前做的一个工具,MooInfo:

https://github.com/rememberber/MooInfo

六、虚拟线程探索

Virtual threads are lightweight threads that reduce the effort of writing, maintaining, and debugging high-throughput concurrent applications.

虚拟线程是轻量级线程,可以减少编写、维护和调试高吞吐量并发应用程序的工作量。

Oracle介绍原文:

https://docs.oracle.com/en/java/javase/20/core/virtual-threads.html#GUID-DC4306FC-D6C1-4BCC-AECE-48C32C1A8DAA

平台线程

Oracle官方文档的机器翻译:

平台线程是作为操作系统(OS)线程的瘦包装器实现的。

平台线程在其底层操作系统线程上运行Java代码,平台线程在平台线程的整个生命周期内捕获其操作系统线程。

因此,可用平台线程的数量受限于操作系统线程的数量。

平台线程通常有一个大的线程堆栈和其他由操作系统维护的资源。

平台线程支持线程局部变量。

平台线程适合运行所有类型的任务,但可能是有限的资源。

虚拟线程

Oracle官方文档的机器翻译:

与平台线程一样,虚拟线程也是 java.lang.Thread 的一个实例。

但是,虚拟线程并不依赖于特定的操作系统线程。

虚拟线程仍然在操作系统线程上运行代码。

但是,当虚拟线程中运行的代码调用阻塞 I/O 操作时,Java 运行时会挂起虚拟线程,直到可以恢复为止。

与挂起的虚拟线程关联的操作系统线程现在可以自由地为其他虚拟线程执行操作。

实现原理

虚拟线程的实现方式与虚拟内存类似。

为了模拟大量内存,操作系统将较大的虚拟地址空间映射到有限的 RAM。

同样,为了模拟大量线程,Java运行时将大量虚拟线程映射到少量操作系统线程。

与平台线程不同,虚拟线程通常具有浅调用堆栈,只执行单个 HTTP 客户端调用或单个 JDBC 查询。

尽管虚拟线程支持线程局部变量,但您应该仔细考虑使用它们,因为单个 JVM 可能支持数百万个虚拟线程。

虚拟线程适合运行大部分时间处于阻塞状态、通常等待 I/O 操作完成的任务。

但是,它们不适用于长时间运行的 CPU 密集型操作。

虚拟线程用法

Thread thread = Thread.ofVirtual().start(() -> System.out.println("Hello"));
thread.join();

或者

        try {
Thread.Builder builder = Thread.ofVirtual().name("MyThread");
Runnable task = () -> {
System.out.println("Running thread");
};
Thread t = builder.start(task);
System.out.println("Thread t name: " + t.getName());
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

或者

public class CreateNamedThreadsWithBuilders {

    public static void main(String[] args) {

        try {
Thread.Builder builder =
Thread.ofVirtual().name("worker-", 0); Runnable task = () -> {
System.out.println("Thread ID: " +
Thread.currentThread().threadId());
}; // name "worker-0"
Thread t1 = builder.start(task);
t1.join();
System.out.println(t1.getName() + " terminated"); // name "worker-1"
Thread t2 = builder.start(task);
t2.join();
System.out.println(t2.getName() + " terminated"); } catch (InterruptedException e) {
e.printStackTrace();
}
}
}

或者

        try (ExecutorService myExecutor =
Executors.newVirtualThreadPerTaskExecutor()) {
Future<?> future =
myExecutor.submit(() -> System.out.println("Running thread"));
future.get();
System.out.println("Task completed");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

以上是Java20文档的用法,实际使用时我发现还可以这样:

 Thread.startVirtualThread(() -> {
// do something });

平台线程和虚拟线程对比测试

为了测试对比,我建了一个项目

初步对比,和官网描述一致,计算密集型场景差别不大,IO密集型场景有明显改善

虚拟线程100个,IO读文件



平台线程100个,IO读文件



虚拟线程100个,Get请求百度首页



平台线程100个,Get请求百度首页

但是由于是本地测试,且用例比较简陋,无法完全得出准确结论。

日后大家有实际IO密集性多线程场景可以实际感受下。

线程池?忘了它吧

开发人员通常会将应用程序代码从基于线程池的传统 ExecutorService 迁移到虚拟线程每任务 ExecutorService。

线程池和所有资源池一样,旨在共享昂贵的资源,

但虚拟线程并不昂贵,而且永远不需要将它们池化。

七、一颗语法糖?Java21 新特性:Record Patterns

一个例子感受一下新特性:Record Patterns

before:

static void printSum(Object obj) {
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
System.out.println(x+y);
}
}

after:

static void printSum(Object obj) {
if (obj instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}

参考:https://my.oschina.net/didispace/blog/10112428

作者:京东科技 周波

来源:京东云开发者社区  转载请注明来源

Java21上手体验-分代ZGC和虚拟线程的更多相关文章

  1. Java 垃圾回收机制 (分代垃圾回收ZGC)

    什么是自动垃圾回收? 自动垃圾回收是一种在堆内存中找出哪些对象在被使用,还有哪些对象没被使用,并且将后者删掉的机制.所谓使用中的对象(已引用对象),指的是程序中有指针指向的对象:而未使用中的对象(未引 ...

  2. Android 7.0真实上手体验

    Android 7.0真实上手体验 Android 7.0的首个开发者预览版发布了,支持的设备只有Nexus6.Nexus 5X.Nexus 6P.Nexus 9.Nexus Player.Pixel ...

  3. JVM垃圾回收算法及分代垃圾收集器

    一.垃圾收集器的分类 1.次收集器 Scavenge GC,指发生在新生代的GC,因为新生代的Java对象大多都是朝生夕死,所以Scavenge GC非常频繁,一般回收速度也比较快.当Eden空间不足 ...

  4. 理解JVM之内存分配以及分代思想实现

    1.基本内存分批策略 大多数情况在新生代Eden区分配,如果启动了本地线程分配缓冲,将按线程优先在TLAB(线程私有缓冲区)上分配.当Eden区域没有足够的空间时将发起一次Minor GC. 值得注意 ...

  5. 支持JDK19虚拟线程的web框架,之一:体验

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于虚拟线程 随着JDK19 GA版本的发布,虚拟线程 ...

  6. JVM系列-分代收集垃圾回收

    Java自动垃圾回收(Automatic Garbage Collection)是自动回收堆上不再使用的内存,new的对象在程序中没有引用指向它,就会被回收.回收的实现很多,有Reference Co ...

  7. JVM内存管理------GC算法精解(五分钟教你终极算法---分代搜集算法)

    引言 何为终极算法? 其实就是现在的JVM采用的算法,并非真正的终极.说不定若干年以后,还会有新的终极算法,而且几乎是一定会有,因为LZ相信高人们的能力. 那么分代搜集算法是怎么处理GC的呢? 对象分 ...

  8. JVM调优-Java垃圾回收之分代回收

    为什么要进行分代回收? JVM使用分代回收测试,是因为:不同的对象,生命周期是不一样的.因此不同生命周期的对象采用不同的收集方式. 可以提高垃圾回收的效率. Java程序运行过程中,会产生大量的对象, ...

  9. 【转】JVM 分代GC策略分析

    我们以Sun HotSpot VM来进行分析,首先应该知道,如果我们没有指定任何GC策略的时候,JVM默认使用的GC策略.Java虚拟机是按照分代的方式来回收垃圾空间,我们应该知道,垃圾回收主要是针对 ...

  10. JVM的stack和heap,JVM内存模型,垃圾回收策略,分代收集,增量收集

    (转自:http://my.oschina.net/u/436879/blog/85478) 在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认 ...

随机推荐

  1. Windows11右键菜单修改为Win10模式的方法

    Windows11右键菜单修改为Win10模式的方法 自述: 更新win11后看着鼠标右键的菜单有些不太舒服,索性就改回了win10的右键菜单的样式 , 下面开始进行操作 第一步 首先以管理员方式打开 ...

  2. 2. 搭建Mybatis

    确认开发环境​ MySQL不同版本的注意事项 1.驱动类driver-class-name MySQL 5版本使用jdbc5驱动,驱动类使用:com.mysql.jdbc.Driver MySQL 8 ...

  3. PostgreSQL 12 文档: PostgreSQL 服务端程序

    PostgreSQL 服务器应用   这一部分包含PostgreSQL服务器应用和支持工具的参考信息.这些命令只在数据库服务器所在的主机上运行才有用.其他工具程序在PostgreSQL 客户端应用中列 ...

  4. 数据库连接池之c3p0-0.9.1.2,16年的古董,发生连接泄露怎么查(一)

    背景 这篇文章是写给有缘人的,为什么这么说呢,因为本篇主要讲讲数据库连接池之c3p0-0.9.1.2版本. 年轻的朋友,可能没怎么听过c3p0了,或者也仅限于听说,这都很正常,因为c3p0算是200几 ...

  5. 十大功能特性,助力开发者玩转API Explorer

    摘要:华为云API Explorer为开发者提供一站式API解决方案统一平台,集成华为云服务所有开放API,支持全量快速检索.可视化调试.帮助文档.代码示例等能力,帮助开发者快速查找.学习API和使用 ...

  6. 现代C++(Modern C++)基本用法实践:N、其他零散的常用特性

    概述 这一篇简单介绍一些其他的比较实用的特性,如果读者想了解现代C++的全部特性,参考:cpp reference 其他特性 预置和弃置函数default&delete 在 C++11 中引入 ...

  7. 投个 3D 冰壶,上班玩一玩

    ​本篇文章将介绍如何使用物理引擎和图扑 3D 可视化技术来呈现冰壶运动的模拟. Oimo.js 物理引擎 Oimo.js 是一个轻量级的物理引擎,它使用 JavaScript 语言编写,并且基于 Oi ...

  8. 【MAUI Blazor踩坑日记】2.关于Windows上的相机问题

    前言 本系列文章,默认你已经踏上了MAUI Blazor的贼船,并且对MAUI Blazor有了一些了解,知道MAUI是什么,知道Blazor是什么. 不会教你怎么写MAUI Blazor的项目,只是 ...

  9. Django学习笔记:第三章D的路由和视图

    1.网站的入口--路由和视图 URL是网站Web服务的入口.用户在浏览器输入URL发出请求后,django会根据路由系统,运行对应的视图函数,然后返回信息到浏览器中. 1.1 认识路由 创建项目时,会 ...

  10. 2021-7-9 VUE的number\trim\lazy

    Vue的v-model.number顾名思义,即是将绑定的参数中的字符串强制转换为int类型 而v-model.trim是将参数的前后空格删除 v-model.lazy:v-model的绑定是实时响应 ...