Erste Schritte mit Java21 – Generations-ZGC und virtuelle Threads



I. Einleitung

Oracle hat erst vor einiger Zeit Java21 veröffentlicht. Da es sich um die neueste LTS-Version handelt, hat sie die Aufmerksamkeit aller auf sich gezogen. Ich habe das Upgrade-Erlebnis auch zum ersten Mal in meinem persönlichen Projekt durchgeführt, um es herauszufinden und es mit allen zu teilen.


2. Einführung in den Inhalt des Java21-Updates

Offizielle Veröffentlichungsankündigung : https://jdk.java.net/21/release-notes

Einführung in Open Source China: https://my.oschina.net/waylau/blog/10112170

Neue Funktionen auf einen Blick:

  • JEP 431: Sequenzsammlungen

  • JEP 439: Generations-ZGC

  • JEP 440: Aufnahmemodus

  • JEP 441: Switch-Pattern-Matching

  • JEP 444: Virtuelle Threads

  • JEP 449: Veraltung des Windows 32-Bit-x86-Ports

  • JEP 451: Bereiten Sie sich darauf vor, das dynamische Laden von Proxys zu deaktivieren

  • JEP 452: Schlüsselkapselungsmechanismus-API

  • JEP 430: String-Vorlagen (Vorschau)

  • JEP 442: Externe Funktionen und Speicher-API (Dritte Vorschau)

  • JEP 443: Unbenannte Muster und Variablen (Vorschau)

  • JEP 445: Unbenannte Klassen- und Instanzhauptmethoden (Vorschau)

  • JEP 446: Bereichsbezogene Werte (Vorschau)

  • JEP 453: Strukturierte Parallelität (Vorschau)

  • JEP 448: Vektor-API (Inkubatorphase 6)

Unter ihnen schenkt jeder dem generationsübergreifenden ZGC und virtuellen Threads mehr Aufmerksamkeit.



3. Auspacken

Download-Link:
OpenJDK-Version: https://jdk.java.net/21/
Oracle-Version: https://www.oracle.com/java/technologies/downloads/

Vergleich 17

Der Rahmen wurde von Edelstahl auf Titan aufgerüstet und die Verzeichnisstruktur ist dieselbe:

Die Anzahl der Module beträgt eins weniger als 17:

Die Gesamtgröße wurde von 289 MB auf 320 MB erhöht



4. Upgrade-Erfahrung

herunterladen

POM aktualisieren

Versuchen Sie es mit Laufen

Antwort:
java.lang.NoSuchFieldError: Die Klasse com.sun.tools.javac.tree.JCTree$JCImport verfügt nicht über das Mitgliedsfeld „com.sun.tools.javac.tree.JCTree qualid“

Lösung: Lombok auf 1.18.30 aktualisieren

Grund: https://github.com/projectlombok/lombok/issues/3393

Kompatibilitätsprüfung:

Da mein Projekt zuvor JDK17 verwendet hat, ist die Kompatibilität dieses Upgrades gut. Ich habe nur eines gefunden:
PopupMenu wurde in der Taskleiste verwendet und es ist ein Zeichensatzproblem aufgetreten:



5. Generationsübergreifende ZGC-Erfahrung

ZGC war auch in früheren JDK-Versionen verfügbar, und diese Generation von ZGC ist noch vielversprechender. Die offizielle Einführung lautet wie folgt:

Anwendungen, die mit Generational ZGC ausgeführt werden, sollten folgende Vorteile bieten:

Geringeres Risiko von Zuteilungsstörungen,

Geringerer erforderlicher Heap-Speicheraufwand und

Geringerer CPU-Overhead für die Garbage Collection.

Aktivieren Sie Generational ZGC mit den Befehlszeilenoptionen -XX:+UseZGC -XX:+ZGenerational

Entwickelt, um die Anwendungsleistung zu verbessern, indem separate Generationen für junge und alte Objekte beibehalten werden. Junge Objekte neigen dazu, schnell zu sterben; die Aufrechterhaltung getrennter Generationen wird es dem ZGC ermöglichen, junge Objekte häufiger zu sammeln. Anwendungen, die auf ZGC der Generation ausgeführt werden, sollten die folgenden Vorteile haben: geringeres Risiko von Zuordnungsausfällen, geringerer Heap-Speicher-Overhead und geringerer CPU-Overhead für die Garbage Collection. Diese Vorteile sollten ohne wesentliche Verschlechterung des Durchsatzes erreicht werden.
Referenz: Ursprünglicher Link: https://blog.csdn.net/qq_35030548/article/details/133047541
Referenz zum Leistungstest: https://inside.java/2023/09/03/roadto21-performance/
JVM-Parameter: -XX:+UseZGC -XX:+ZGenerational
Verwendung von Java21, nicht Verwendung von ZGC
Ansicht der MooInfo-Speichernutzung


Verwenden Sie mit Java21 Generations-ZGC

Ansicht der MooInfo-Speichernutzung


Das Obige ist nur eine vorläufige Erfahrung. Weitere Inhalte zu ZGC, wie beispielsweise detailliertes Generationsrecycling, werden in Zukunft weiter untersucht.

Die oben genannte Speichernutzung wird mit einem Tool überprüft, das ich zuvor erstellt habe, MooInfo:
https://github.com/rememberber/MooInfo



6. Erkundung virtueller Threads

Virtuelle Threads sind leichtgewichtige Threads, die den Aufwand für das Schreiben, Warten und Debuggen gleichzeitiger Anwendungen mit hohem Durchsatz reduzieren.

Virtuelle Threads sind leichtgewichtige Threads, die den Aufwand für das Schreiben, Warten und Debuggen gleichzeitiger Anwendungen mit hohem Durchsatz reduzieren.

Ursprüngliche Einführung in Oracle: https://docs.oracle.com/en/java/javase/20/core/virtual-threads.html#GUID-DC4306FC-D6C1-4BCC-AECE-48C32C1A8DAA

Plattform-Thread

Maschinelle Übersetzung der offiziellen Oracle-Dokumentation:

Plattform-Threads werden als Thin Wrapper für Betriebssystem-Threads implementiert.
Plattform-Threads führen Java-Code auf ihren zugrunde liegenden Betriebssystem-Threads aus, und Plattform-Threads erfassen den Betriebssystem-Thread des Plattform-Threads während seiner gesamten Lebensdauer.
Daher ist die Anzahl der verfügbaren Plattform-Threads durch die Anzahl der Betriebssystem-Threads begrenzt.
Plattform-Threads verfügen normalerweise über einen großen Thread-Stack und andere vom Betriebssystem verwaltete Ressourcen.
Plattform-Threads unterstützen Thread-lokale Variablen.
Plattform-Threads eignen sich zum Ausführen aller Arten von Aufgaben, verfügen jedoch möglicherweise über begrenzte Ressourcen.
virtueller Thread

Maschinelle Übersetzung der offiziellen Oracle-Dokumentation:

Wie Plattform-Threads ist ein virtueller Thread eine Instanz von java.lang.Thread.
Virtuelle Threads sind jedoch nicht an bestimmte Betriebssystem-Threads gebunden.
Virtuelle Threads führen weiterhin Code auf Betriebssystem-Threads aus.
Wenn jedoch in einem virtuellen Thread ausgeführter Code einen blockierenden E/A-Vorgang aufruft, hält die Java-Laufzeit den virtuellen Thread an, bis er wieder aufgenommen werden kann.
Der Betriebssystem-Thread, der dem angehaltenen virtuellen Thread zugeordnet ist, kann nun Vorgänge für andere virtuelle Threads ausführen.

Umsetzungsprinzip

  • Virtuelle Threads werden ähnlich wie virtueller Speicher implementiert.

  • Um große Speichermengen zu simulieren, ordnet das Betriebssystem einen großen virtuellen Adressraum einer begrenzten Menge RAM zu.

  • Um eine große Anzahl von Threads zu simulieren, ordnet die Java-Laufzeitumgebung eine große Anzahl virtueller Threads einer kleinen Anzahl von Betriebssystem-Threads zu.

  • Im Gegensatz zu Plattform-Threads verfügen virtuelle Threads typischerweise über flache Aufrufstapel und führen nur einen einzelnen HTTP-Client-Aufruf oder eine einzelne JDBC-Abfrage aus.

  • Obwohl virtuelle Threads threadlokale Variablen unterstützen, sollten Sie deren Verwendung sorgfältig abwägen, da eine einzelne JVM möglicherweise Millionen virtueller Threads unterstützt.

虚拟线程适合运行大部分时间处于阻塞状态、通常等待 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}}

Supongo que te gusta

Origin my.oschina.net/u/4090830/blog/10320755
Recomendado
Clasificación