Java21 入門エクスペリエンス - 世代別 ZGC と仮想スレッド



I.はじめに

Oracle は少し前に Java21 をリリースしましたが、これは最新の LTS バージョンであるため、注目を集めています。また、それを見つけてみんなと共有するために、個人プロジェクトで初めてアップグレード体験を実施しました。


2. Java21アップデート内容のご紹介

公式リリースの発表: http://jdk.java.net/21/release-notes

オープンソース中国の紹介: https://my.oschina.net/waylau/blog/10112170

新機能の概要:

  • JEP 431: シーケンス コレクション

  • JEP 439: 世代別 ZGC

  • JEP 440: 録音モード

  • JEP 441: スイッチパターンマッチング

  • JEP 444: 仮想スレッド

  • JEP 449: Windows 32 ビット x86 ポートの廃止

  • JEP 451: プロキシの動的ロードを無効にする準備をする

  • JEP 452: キーカプセル化メカニズム API

  • JEP 430: 文字列テンプレート (プレビュー)

  • JEP 442: 外部関数とメモリ API (第 3 プレビュー)

  • JEP 443: 名前のないパターンと変数 (プレビュー)

  • JEP 445: 名前のないクラスおよびインスタンスのメイン メソッド (プレビュー)

  • JEP 446: スコープ付きの値 (プレビュー)

  • JEP 453: 構造化同時実行 (プレビュー)

  • JEP 448: ベクター API (インキュベーター フェーズ 6)

その中でも誰もが注目しているのは世代別の ZGC と仮想スレッドです。



3. 開梱

ダウンロードリンク:
OpenJDK バージョン: https://jdk.java.net/21/
Oracle バージョン: https://www.oracle.com/java/technologies/downloads/

比較17

フレームはステンレススチールからチタンにアップグレードされ、ディレクトリ構造は同じです。

モジュールの数は 17 より 1 つ少ない数です。

全体のサイズが 289MB から 320MB に増加



4. アップグレード体験

ダウンロード

アップデートポン

走ってみる

実行中:
java.lang.NoSuchFieldError: クラス com.sun.tools.javac.tree.JCTree$JCImport にはメンバー フィールド 'com.sun.tools.javac.tree.JCTree qualid' がありません

解決策: lombok を 1.18.30 にアップグレードします。

理由: https://github.com/projectlombok/lombok/issues/3393

互換性チェック:

私のプロジェクトでは以前 JDK17 を使用していたので、今回のアップグレードの互換性は良好ですが、
システム トレイで PopupMenu が使用されており、文字セットの問題が発生したことだけがわかりました。



5. 世代間の ZGC 経験

ZGC は以前の JDK バージョンでも利用できましたが、この世代の ZGC はさらに有望です。

世代別 ZGC で実行されるアプリケーションには、次の利点があります。

割り当てが失速するリスクが低くなり、

必要なヒープ メモリのオーバーヘッドが低下し、

ガベージ コレクションの CPU オーバーヘッドが減少します。

コマンド ライン オプション -XX:+UseZGC -XX:+ZGenerational で世代別 ZGC を有効にする

若いオブジェクトと古いオブジェクトの世代を別々に維持することで、アプリケーションのパフォーマンスを向上させるように設計されています。若いオブジェクトはすぐに消滅する傾向があるため、別々の世代を維持すると、ZGC がより頻繁に若いオブジェクトを収集できるようになります。世代別 ZGC 上で実行されているアプリケーションには、割り当て停止のリスクの低減、ヒープ メモリのオーバーヘッドの低減、ガベージ コレクションの CPU オーバーヘッドの低減という利点が得られます。これらの利点は、スループットを大幅に低下させることなく達成される必要があります。
参考:元リンク:https://blog.csdn.net/qq_35030548/article/details/133047541
パフォーマンス テストのリファレンス: https://inside.java/2023/09/03/roadto21-performance/
JVM パラメータ: -XX:+UseZGC -XX:+ZGenerational
ZGC を使用せず、Java21 を使用する
MooInfo のメモリ使用量ビュー


Java21 を使用し、世代別 ZGC を使用する

MooInfo のメモリ使用量ビュー


上記は単なる予備的な体験であり、詳細な世代リサイクルなど、ZGC に関するさらに多くのコンテンツは、将来さらに検討される予定です。

上記のメモリ使用量は以前作成したツール MooInfo を使用して確認しています:
https://github.com/rememberber/MooInfo



6. 仮想スレッドの探索

仮想スレッドは、高スループットの同時アプリケーションの作成、保守、デバッグの労力を軽減する軽量のスレッドです。

仮想スレッドは、高スループットの同時アプリケーションの作成、保守、デバッグの労力を軽減する軽量のスレッドです。

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);    }}
参考资料:
[1] https://my.oschina.net/didispace/blog/10112428
-end-


本文分享自微信公众号 - 京东云开发者(JDT_Developers)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

IntelliJ IDEA 2023.3 & JetBrains 全家桶年度大版本更新 新概念“防御性编程”:让自己稳拿铁饭碗 GitHub.com 跑了 1200 多台 MySQL 主机,如何无缝升级到 8.0? 周星驰 Web3 团队下个月上线独立 App Firefox 会被淘汰吗? Visual Studio Code 1.85 发布,浮动窗口 余承东:华为明年将推出颠覆性产品,改写行业历史 美国 CISA 建议放弃 C/C++,消除内存安全漏洞 TIOBE 12 月:C# 有望成为年度编程语言 雷军 30 年前写的论文:《计算机病毒判定专家系统原理与设计》
{{o.name}}
{{m.name}}

おすすめ

転載: my.oschina.net/u/4090830/blog/10320755
ZGC