Java 16新機能まとめ

Java 16が2021/3/16にリリースされました。

http://mail.openjdk.java.net/pipermail/jdk-dev/2021-March/005188.html

多くのプログラマに関係ありそうな変更は、record、instanceofパターンマッチの正式化、Stream.toListです。またTLS1.0と1.1が無効になっているので古いMySQLなどへの接続でハマることがあるかもしれません。

詳細はこちら
[Java SE 16 Platform JSR 391](http://openjdk.java.net/projects/jdk/16/spec/)
[JDK 16 GA Release](http://jdk.java.net/16/)

APIドキュメントはこちら
[Overview (Java SE 16)](https://cr.openjdk.java.net/~iris/se/16/latestSpec//api/index.html)



MacやLinuxでのインストールには[SDKMAN!](https://sdkman.io/)をお勧めします

Oracle OpenJDK以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。

- [AdoptOpenJDK](https://adoptopenjdk.net/?variant=openjdk16&jvmVariant=hotspot)
- [Liberica JDK](https://bell-sw.com/pages/downloads/?version=java-16)
- [Amazon Corretto 16](https://github.com/corretto/corretto-jdk/releases/tag/16.0.0.36.1)
- [Microsoft Build of OpenJDK](https://www.microsoft.com/openjdk)

アップデートは4月に16.0.1が、翌年7月に16.0.2がリリースされることになります。

Oracle JDKは開発用途には無償利用できますが、商用利用にはJava SE Subscriptionを購入するなどのライセンスが必要になります。

- [Java SE Development Kit 16](https://www.oracle.com/java/technologies/javase-jdk16-downloads.html)

# JEP
大きめの変更はJEPでまとまっています。
http://openjdk.java.net/projects/jdk/16/

今回は17個のJEPが取り込まれました。すでにプレビューなどで出ていたものが5つあり、新たに取り込まれたものは12です。
[JEP 338: Vector API (Incubator)](https://openjdk.java.net/jeps/338)
[JEP 347: Enable C++14 Language Features](https://openjdk.java.net/jeps/347)
[JEP 357: Migrate from Mercurial to Git](https://openjdk.java.net/jeps/357)
[JEP 369: Migrate to GitHub](https://openjdk.java.net/jeps/369)
[JEP 376: ZGC: Concurrent Thread-Stack Processing](https://openjdk.java.net/jeps/376)
[JEP 380: Unix-Domain Socket Channels](https://openjdk.java.net/jeps/380)
[JEP 386: Alpine Linux Port](https://openjdk.java.net/jeps/386)
[JEP 387: Elastic Metaspace](https://openjdk.java.net/jeps/387)
[JEP 388: Windows/AArch64 Port](https://openjdk.java.net/jeps/388)
[JEP 389: Foreign Linker API (Incubator)](https://openjdk.java.net/jeps/389)
[JEP 390: Warnings for Value-Based Classes](https://openjdk.java.net/jeps/390)
[JEP 392: Packaging Tool](https://openjdk.java.net/jeps/392)
[JEP 393: Foreign-Memory Access API (Third Incubator)](https://openjdk.java.net/jeps/393)
[JEP 394: Pattern Matching for instanceof](https://openjdk.java.net/jeps/394)
[JEP 395: Records](https://openjdk.java.net/jeps/395)
[JEP 396: Strongly Encapsulate JDK Internals by Default](https://openjdk.java.net/jeps/396)
[JEP 397: Sealed Classes (Second Preview)](https://openjdk.java.net/jeps/397)

分野ごとにまとめていきます。

# 言語機能
言語仕様にかかわる変更としては次のようなものがあります。
Pattern Matching for instanceofとRecordsは正式化されました。
プレビューで残ってるのはSealed Classesだけですね。これも17LTSでは正式化されるはずで、プレビュー機能を残さないようにしているのだと思います。
[JEP 394: Pattern Matching for instanceof](https://openjdk.java.net/jeps/394)
[JEP 395: Records](https://openjdk.java.net/jeps/395)
[JEP 397: Sealed Classes (Second Preview)](https://openjdk.java.net/jeps/397)
[JEP 390: Warnings for Value-Based Classes](https://openjdk.java.net/jeps/390)

## Inner ClassでStaticメンバーの定義
RecordがInner Classで定義できなかったので、その原因になるstaticメンバーの制限が緩和されています。

``` java
public class Outer {
  class Inner {
    static final String MESSAGE = "hoge";
    static String message() {
      return MESSAGE;
    }
  }
}
```

## [JEP 394: Pattern Matching for instanceof](https://openjdk.java.net/jeps/394)


パターンマッチングです。
instanceofを使ったパターンマッチが14でプレビューとして入り、そのまま変更なしにJava 15でセカンドプレビューになりました。
Java 16では、パターン変数が変更可能になりました。これはローカル変数との非対称性を減らすためということです。
また、いままでString型のsに対して`s instanceof Object o`のような常に真となるマッチも可能でしたが、Java 16からはコンパイルエラーになります。

```
jshell> var s = ""
s ==> ""

jshell> s instanceof Object o
|  エラー:
|  パターン・タイプjava.lang.Objectは式タイプjava.lang.Stringのサブタイプです
|  s instanceof Object o
|  ^--
```

`値 instanceof パターン`で、値をマッチさせることができます。
パターンは、定数か変数定義です。変数定義の場合には、型が一致していた場合にtrueになりその変数に値が割り当てられます。

```java
if (x instanceof Integer i) {
    // can use i here
}
```

switchで使えるようになれば便利ですが、これは別のJEPで定義されていて、Java 17以降に持ち越されています。17LTSにはプレビュー機能は入らないと思うので、来年3月のJava 18ですかね。
[JEP draft: Pattern matching for switch (Preview)](http://openjdk.java.net/jeps/8213076)

今後は、Recordsと組み合わせて`record Point(int x, int y){}`があるときに`p instanceof(var x, var y)`のような分解のパターンも使えるようにするようです。
また、`Optional.of`のようなビルダーに対して同様にパターンを使えることも考えてるようです。つまり`os instanceof Optional.of(String s)`のようなパターンが書けるようにするということです。
楽しみですね。
https://github.com/openjdk/amber-docs/blob/master/site/design-notes/pattern-match-object-model.md

分解のパターンについてはこちら。
http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html

## [JEP 395: Records](https://openjdk.java.net/jeps/395)
レコードが標準化される
Java 14で[JEP 359](https://openjdk.java.net/jeps/359)としてPreviewになりJava 15で[JEP 384](https://openjdk.java.net/jeps/384)として少し変更を加えてSecond Previewになっていました。
Java 16では、インナークラス内でも定義可能にして標準化されました。

### 定義

recordとして定義します。

```java
record Foo(int x, int y) {}
```

他のクラスは継承できません。

```
record レコード名(状態) { 定義 }
```

これは次のようなクラスになります。

```java
class Foo extends Record {
  // 状態がprivate finalとして定義
  private final int x;
  private final int y;
  // 追加のインスタンスフィールドは定義できない

  // 状態をフィールドに設定するコンストラクタが定義される
  public Foo(int x, int y) {
    this.x = x;
    this.y = y;
  }

  // 状態と同じ名前のメソッドが定義される
  public int x() {
    return x;
  }
  public int y() {
    return y;
  }

  // 状態を反映するhashCodeが定義される
  public int hashCode() { ... }
  // 状態を比較するequals が定義される
  public boolean equals() { ... }
  // 状態を表示するtoStringが定義される
  public String toString() { ... }
}
```

取得用メソッドは定義されますが、値設定用のメソッドは定義されません。つまり、イミュータブルなオブジェクトとなります。また、get/setではないことからJava Beanとの互換性もありません。
`hashCode()`メソッドや`equals()`メソッドなどは実際にはinvokeDynamicで実行時に実装コードが生成されます。

次のように、状態の検査や正規化にコンストラクタが定義できます。コンストラクタの引数リストは省略できます。

```java
record Range(int lo, int hi) {
  Range {
    if (lo < 0) lo = 0; // 正規化
    if (lo > hi) throw IllegalArgumentException();
    // 設定されなかったフィールドはあとで設定される
  }
}
```

また、コンストラクタの引数リストを省略した場合、コンポーネントフィールドに値を割り当てるとコンパイルエラーになります。

```java
record Range(int lo, int hi) {
  Range {
    this.lo = 3; // コンパイルエラー
  }
}
```

Java 15までではstaticではないinner classの中でrecordを定義することはできませんでしたが、この制約が今回のJava 16で外れました。

```java
public class NestedRecord {
  class Foo {
      record Bar(int x){}
  }
}
```

また、Java 16ではコンポーネント定義にC形式の配列宣言が使えなくなっています。

```
jshell> record T(int a[]){}
|  エラー:
|  旧式の配列表記法はレコード・コンポーネントでは使用できません
|  record T(int a[]){}
```

### APIの拡張

recordは`java.lang.Record`を継承したクラスになります。

次のようにRecordを継承するコードを書こうとすると`records cannot directly extend java.lang.Record`というエラーになります。

```java
class Foo extends Record {
}
```

`Class`クラスにはレコード関連のメソッドが追加されています。
`isRecord`メソッドで型がrecordかどうか判定できます。また`getRecordComponents`でrecordで定義されたコンポーネントを取得することができます。ただし、値の取得はRecordComponent経由では行えないので、Field経由で行うようです。

```
jshell> Foo.class.isRecord()
$9 ==> true

jshell> Foo.class.getRecordComponents()
$10 ==> RecordComponent[2] { int x, int y }

jshell> String.class.isRecord()
$11 ==> false

jshell> String.class.getRecordComponents()
$12 ==> null
```

### 型名としての`record`の制限

`record`という名前を型(クラス・インタフェース・レコード型など)につけることは制限されています。

```
$ jshell
|  JShellへようこそ -- バージョン16
|  概要については、次を入力してください: /help intro

jshell> class record{}
|  エラー:
|  ここでは'record'は許可されません
|    リリース14から'record'は制限された型名であり、型の宣言に使用できません
|  class record{}
|        ^

jshell> String record=""
record ==> ""

jshell> record Rec(int record){}

jshell> Rec record=new Rec(3)
record ==> Rec[record=3]
```

enumと違いキーワードではないので、変数名やフィールド、recordのコンポーネント名にはrecordを使えます。

## [JEP 397: Sealed Classes (Second Preview)](https://openjdk.java.net/jeps/397)

Sealedクラスは、継承できるクラスを限定する機能です。仕様の名前は「クラス」  となってますが、interfaceでも使えます。
クラス名のあとに `permits`で継承クラスを指定していくという感じになります。

```java
public abstract sealed class Shape
    permits Circle, Rectangle, Square {...}
```

そうするとShapeクラスを継承できるのはCircle、Rectangle、Squareに限定されます。Circle、Rectangle、Squareは必ず定義する必要があります。
これらのクラスは、モジュール化しているのであれば同じモジュール内、モジュール化していないのであれば同じパッケージ内である必要があります。
また、permitsされるクラスが同一のソースだけにあるのであれば、permitsは省略できます。
permitsするクラスは定義のときにsealedかnon-sealedかfinalをつける必要があります。recordは暗黙的にfinalになるのでなにもつける必要はありません。

現状ではほとんど使いどころがないです。
例えば、Shapeの継承先は確定しているので、次のコードはコンパイル通ってほしいところですが、エラーになります。

```java
String getName(Shape s) {
  if (s instanceof Circle) {
    return "円";
  } else if (s instanceof Rectangle) {
    return "四角";
  } else if (s instanceof Square) {
    return "正方形";
  }
}
```

まだドラフトですが[Pattern matching for switch (Preview)](http://openjdk.java.net/jeps/8213076)が導入されてswitch式/文でパターンマッチが使えるようになれば、次のようにdefaultなしで書ける予定です。

```java
String getName(Shape s) {
  switch (s) {
    case Circle c -> return "円";
    case Rectangle r -> return "四角";
    case Square q -> return "正方形";
  }
}
```

もちろん、ここでswitch式を使うこともできますね。

```java
String getName(Shape s) {
  return switch (s) {
    case Circle c -> "円";
    case Rectangle r -> "四角";
    case Square q -> "正方形";
  }
}
```

ということで、現時点でのSealedクラスは今後の拡張のための下準備というところです。
背景にある考え方は、次の記事で言語設計者であるBrian Goetzが解説しています。
[Java 注目の機能：Sealed クラス](https://www.infoq.com/jp/articles/java-sealed-classes/)

## [JEP 390: Warnings for Value-Based Classes](https://openjdk.java.net/jeps/390)
Integerなどvalue-based classをsynchronizedのロックに使っているとコンパイル時・実行時に警告がでる

```
jshell> synchronized (LocalDateTime.now()){}
|  警告:
|  値ベース・クラスのインスタンスで同期しようとしました
|  synchronized (LocalDateTime.now()){}
|  ^----------------------------------^
```

# API
APIの変更に関するJEPは次のようなものがあります。
[JEP 380: Unix-Domain Socket Channels](https://openjdk.java.net/jeps/380)
[JEP 338: Vector API (Incubator)](https://openjdk.java.net/jeps/338)
[JEP 389: Foreign Linker API (Incubator)](https://openjdk.java.net/jeps/389)
[JEP 393: Foreign-Memory Access API (Third Incubator)](https://openjdk.java.net/jeps/393)
[JEP 396: Strongly Encapsulate JDK Internals by Default](https://openjdk.java.net/jeps/396)
[\[JDK-8180352\] Add Stream.toList() method - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8180352)
[Stream.mapMulti](https://cr.openjdk.java.net/~iris/se/16/latestSpec/api/java.base/java/util/stream/Stream.html#mapMulti%28java.util.function.BiConsumer%29)
[\[JDK-8254876\] (fs) NullPointerException not thrown when first argument to Path.of or Paths.get is null - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8254876)

APIの変更はこちらにまとまっています。
https://cr.openjdk.java.net/~iris/se/16/latestSpec/apidiffs/overview-summary.html

## [JEP 380: Unix-Domain Socket Channels](https://openjdk.java.net/jeps/380)
SocketChannelやServerSocketChannelでUNIXドメインに対応する。  
同じシステム内でのプロセス間通信でTCP/IPよりも効率がよく、システムをまたがって通信しないのであればセキュアになる。
同一マシン上のコンテナ間でも共有ボリューム経由で効率的な通信ができる。

## [JEP 338: Vector API (Incubator)](https://openjdk.java.net/jeps/338)
AVX命令のような、複数のデータに対する計算を同時に行う命令をJavaから利用できるようになります。
Project Panamaのひとつとして開発されていました。
今回インキュベータとしてJava 16に導入されました。

使うためには実行時やコンパイル時に`--add-modules jdk.incubator.vector`をつける必要があります。

```java
import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;

void vectorComputation(float[] a, float[] b, float[] c) {

    for (int i = 0; i < a.length; i += SPECIES.length()) { // SPECIES.length() = 256bit / 32bit -> 8
        VectorMask<Float> m = SPECIES.indexInRange(i, a.length); // 端数がマスクされる
                                                                 // a.lengthが11でiが8のとき最初の3つしか要素がないので [TTT.....]
		// FloatVector va, vb, vc;
        FloatVector va = FloatVector.fromArray(SPECIES, a, i, m);
        FloatVector vb = FloatVector.fromArray(SPECIES, b, i, m);
        FloatVector vc = va.mul(va).
                    add(vb.mul(vb)).
                    neg();
        vc.intoArray(c, i, m);
    }
}
```

利用できるのは次の6つの型です。それぞれに対応するVector型があって、これが基本になります。

|型|bit幅|Vector|
|--|--|--|
|byte|8|ByteVector|
|short|16|ShortVector|
|int|32|IntVector|
|long|64|LongVector|
|float|32|FloatVector|
|double|64|DoubleVector|

ただ、利用するにはVectorSpeciesが必要です。利用したいVectorにSPECIES_\*という定数が用意されているので、それを使います。\*は一度に計算するbit数ですね。

```
jshell> FloatVector.SP
SPECIES_128         SPECIES_256         SPECIES_512         SPECIES_64          SPECIES_MAX         SPECIES_PREFERRED   
```

MAXではそのハードウェアで使える最大、PREFERREDは推奨ビット数だけど、同じになるんじゃないのかな。ここでは256bitが推奨されて、floatが8個同時に計算できるようになっていますね。

```
jshell> FloatVector.SPECIES_PREFERRED
$11 ==> Species[float, 8, S_256_BIT]
```

ハードウェアで使えるbit数は搭載CPUに依存しますが、普通のIntel/AMDであれば256、XEONとか つよつよCPUなら512かな。M1は128でした。ハードウェアでサポートされないbit数を使おうとするとソフトウェア処理になるので遅くなります。

実際のVectorはfrom*というメソッドで取得します。fromArray、fromByteArray、fromByteBufferが用意されています。インキュベータに入る前はfromValuesがあったのですが、なくなってますね。

Vectorを得られたら、用意されたメソッドで計算します。ひととおりの算術命令はあります。

```
jshell> va.
abs()                   add(                    addIndex(               bitSize()               blend(                  
broadcast(              byteSize()              compare(                div(                    elementSize()           
elementType()           eq(                     equals(                 fma(                    getClass()              
hashCode()              intoArray(              intoByteArray(          intoByteBuffer(         lane(                   
lanewise(               length()                lt(                     max(                    min(                    
mul(                    neg()                   notify()                notifyAll()             pow(                    
rearrange(              reduceLanes(            reduceLanesToLong(      reinterpretAsBytes()    reinterpretShape(       
selectFrom(             shape()                 slice(                  sqrt()                  sub(                    
test(                   toArray()               toDoubleArray()         toIntArray()            toLongArray()           
toShuffle()             toString()              unslice(                viewAsFloatingLanes()   viewAsIntegralLanes()   
wait(                   withLane(      
```

ところで、こういったメソッド呼び出しの内部でAVX命令などを呼び出すのでは遅くなるんではという気がしますが、実際にはJVM intrinsicsという仕組みでJITコンパイラがこれらのメソッド呼び出しをネイティブ関数呼び出しに置き換えます。


## [JEP 393: Foreign-Memory Access API (Third Incubator)](https://openjdk.java.net/jeps/393)

変更点
MemorySegmentとMemoryAddressの明確な分離
MemoryAccessインタフェースの導入。VarHandle APIの利用を最小限にする
共有セグメントのサポート
Cleanerつきセグメント

ヒープ外のメモリをアクセスする方法としては、ByteBufferを使う方法やUnsafeを使う方法、JNIを使う方法がありますが、それぞれ一長一短があります。
ByteBufferでdirect bufferを使う場合、intで扱える範囲の2GBまでに制限されたり、メモリの解放がGCに依存したりします。
Unsafeの場合は、性能もいいのですが、名前が示すとおり安全ではなく、解放済みのメモリにアクセスすればJVMがクラッシュします。
JNIを使うとCコードを書く必要があり、性能もよくないです。

ということで、ヒープ外のメモリを直接扱うAPIがJava 14でインキュベータモジュールとして導入されたわけです。そしてJava 15でセカンドインキュベータになっています。
次のようなコードになります。

```java
import jdk.incubator.foreign.*;
import java.nio.ByteOrder;

VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   for (int i = 0 ; i < 25 ; i++) {
        intHandle.set(seg, i * 4, i);
   }
}
```

コンパイル、実行では`--add-modules jdk.incubator.foreign`を付けてモジュールを読み込む必要があります。
パッケージもモジュール名と同様、`jdk.incubator.foreign`になっています。

MemoryHandlesからwithOffsetとwithStrideが消えましたね。

```
jshell> import jdk.incubator.foreign.*

jshell> MemoryHandles.
asAddressVarHandle(   asUnsigned(           class                 collectCoordinates(   dropCoordinates(
filterCoordinates(    filterValue(          insertCoordinates(    permuteCoordinates(   varHandle(
```

Java 15ではMemoryHandlesからVarHandleを得るメソッドが拡充してました。

```
jshell> MemoryHandles.
asAddressVarHandle(   asUnsigned(           class                 collectCoordinates(   dropCoordinates(
filterCoordinates(    filterValue(          insertCoordinates(    permuteCoordinates(   varHandle(
withOffset(           withStride(
```

Java 14では次の3つのメソッドしかありませんでした。

```
jshell> MemoryHandles.
class         varHandle(    withOffset(   withStride(
```

Java 16ではMemoryAccessというユーティリティが用意されて、VarHandleを使わずにシンプルにかけるようになっています。

```java
import jdk.incubator.foreign.*;

try (MemorySegment segment = MemorySegment.allocateNative(100)) {
   for (int i = 0 ; i < 25 ; i++) {
        MemoryAccess.setIntAtOffset(i * 4, i);
   }
}
```
## [JEP 389: Foreign Linker API (Incubator)](https://openjdk.java.net/jeps/389)
ネイティブライブラリの呼び出しを行う
外部メモリのアクセスにはForeign Memory Access APIを使う
JNIの代替

## [JEP 396: Strongly Encapsulate JDK Internals by Default](https://openjdk.java.net/jeps/396)

sun.misc.Unsafeなど以外の内部APIの隠蔽をデフォルトにします。
内部APIから標準APIへの移行がやりやすいようにします。

Java 9で内部APIを隠蔽する仕組みが導入されていましたが、デフォルトでは無効になっていました。それが今回有効化されました。

次のオプションを利用します。15まではpermitがデフォルトでしたが、denyがデフォルトになりました。

|オプション|APIの利用|警告|スタックトレース|デフォルト|
|---|---|---|---|---|
|--illegal-access=permit|可|||9から15まで|
|--illegal-access=warn|可|出力|||
|--illegal-access=debug|可|出力|出力||
|--illegal-access=deny|不可|||16でデフォルト|

制限される内部APIの一覧はこちらです。
https://cr.openjdk.java.net/~mr/jigsaw/jdk8-packages-denied-by-default

```
jshell> A.class.getClassLoader().loadClass("com.sun.beans.finder.ClassFinder").getMethod("findClass", String.class).invoke("test")
|  例外java.lang.IllegalAccessException: class REPL.$JShell$15 cannot access class com.sun.beans.finder.ClassFinder (in module java.desktop) because 
module java.desktop does not export com.sun.beans.finder to unnamed module @77556fd
|        at Reflection.newIllegalAccessException (Reflection.java:385)
|        at AccessibleObject.checkAccess (AccessibleObject.java:687)
|        at Method.invoke (Method.java:559)
|        at (#5:1)
```

## [\[JDK-8180352\] Add Stream.toList() method - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8180352)
JavaDoc: [Stream.toList](https://cr.openjdk.java.net/~iris/se/16/latestSpec/api/java.base/java/util/stream/Stream.html#toList%28%29)
collect(Collectors.toList)がめんどくさすぎたので、toListが導入されました。
変更不能なListになってますね。

```
C:\Users\naoki>java\jdk\jdk-16-ea31\bin\jshell
|  JShellへようこそ -- バージョン16-ea
|  概要については、次を入力してください: /help intro

jshell> Stream.of("a","b").map(String::toUpperCase).toList()
$1 ==> [A, B]

jshell> $1.getClass()
$2 ==> class java.util.ImmutableCollections$ListN
```

いままでStreamとCollectionフレームワークを分離したいという意向で、Collectors経由でのtoListを行っていたのですが、なにをきっかけに気がかわったのか、突然とりこまれました。
BTSは2017年からありますね。

http://mail.openjdk.java.net/pipermail/core-libs-dev/2020-November/070782.html
https://github.com/openjdk/jdk/pull/1026

## [Stream.mapMulti](https://cr.openjdk.java.net/~iris/se/16/latestSpec/api/java.base/java/util/stream/Stream.html#mapMulti%28java.util.function.BiConsumer%29)

値を次々に生成するような処理をStream中にはさみたい場合に使います。

```
Stream.of("a", "b")
  .mapMulti((s, sink) -> sink.accept(s.toUpperCase()))
  .toList()
```

ここで、sにはStreamを流れる値で、sinkに対してacceptしたものが次に渡されていきます。
http://mail.openjdk.java.net/pipermail/core-libs-dev/2020-July/067560.html

## [\[JDK-8254876\] (fs) NullPointerException not thrown when first argument to Path.of or Paths.get is null - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8254876)

Path.ofに複数の引数わたしたとき、最初の引数がnullだと変なオブジェクトが生成されていました。このPathを使うところでぬるぽが発生していたはず。

```
C:\Users\naoki>jshell
|  JShellへようこそ -- バージョン15
|  概要については、次を入力してください: /help intro

jshell> Path.of(null, "r")
$1 ==> null\r
```

Java 16では最初の引数がnullだったらぬるぽ出るようになっています。

```
C:\Users\naoki>java\jdk\jdk-16\bin\jshell
|  JShellへようこそ -- バージョン16
|  概要については、次を入力してください: /help intro

jshell> Path.of(null, "r")
|  例外java.lang.NullPointerException
|        at Objects.requireNonNull (Objects.java:208)
|        at WindowsFileSystem.getPath (WindowsFileSystem.java:214)
|        at Path.of (Path.java:147)
|        at (#1:1)
```

# JVM

[JEP 376: ZGC: Concurrent Thread-Stack Processing](https://openjdk.java.net/jeps/376)
[JEP 387: Elastic Metaspace](https://openjdk.java.net/jeps/387)
[\[JDK-8255616\] Removal of experimental features AOT and Graal JIT - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8255616)
[\[JDK-8202343\] Disable TLS 1.0 and 1.1 - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8202343)

## [JEP 376: ZGC: Concurrent Thread-Stack Processing](https://openjdk.java.net/jeps/376)
ZGCではPause Mark StartフェーズとPause Relocate Startフェーズでスレッドスタックのスキャンを行っていた。このふたつのフェーズはstop-the-worldを行う。
つまりアプリケーションのルートセットサイズがこのふたつのフェーズの停止時間を決めていた。
アプリケーションスレッドと並列にすることでSTWではスレッドスタックがスキャンされなくなる。つまりSTWがアプリケーションのルートセットのサイズへの依存が少なくなる。
16TBでも1ms以下の停止時間でZGCを使える

スレッドスタックの処理をsafepointからconcurrentフェーズに移動する。
スタック処理を遅延させて並列でインクリメンタルにする
他のスレッドごとのルート処理をZGC safepointから削除する
他のHotSpotのサブシステムがスタックを遅延実行できるような仕組を提供する

典型的なマシンで1ミリ秒以下でsafepointの処理が行えるようにする

大量にスレッドがあるとスレッドごとのルート処理が問題になる


## [JEP 387: Elastic Metaspace](https://openjdk.java.net/jeps/387)
クラスのデータなど、JVMが利用するデータはMetaspaceに保存されますが、このようなデータが使用済みになったときに返却するようにして、効率的にMetaspaceが利用できるようにします。

## [\[JDK-8255616\] Removal of experimental features AOT and Graal JIT - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8255616)
Experimentalとして入っていたAOTコンパイラやGraal JITコンパイラがOracleのOpenJDKビルドからは削除されているようです。
つまり、java.netでダウンロードしたバイナリには含まれなくなっています。他のディストリビューションがどうなっているか確認していないけど、恐らく無効にするんじゃなかろうか。GraalVMはデフォルトでGraal JITを使うので残りますね。
ソースコードには残っているようなので、自分でビルドすれば試すことができます。

## [\[JDK-8202343\] Disable TLS 1.0 and 1.1 - Java Bug System](https://bugs.openjdk.java.net/browse/JDK-8202343)
TLS 1.0と1.1が無効化されています。
そのため、古いバージョンのMySQLへの接続などがエラーになることがあるようです。

# ツール
[JEP 392: Packaging Tool](https://openjdk.java.net/jeps/392)

## [JEP 392: Packaging Tool](https://openjdk.java.net/jeps/392)
Incubatorとして入っていたパッケージングツールが標準化する。


Javaアプリケーションのインストーラを作るツールが入りました。
関連APIも追加されていますが、Incubatorモジュールになっています。
Windowsではlight.exeとcandle.exeが必要なのでhttps://wixtoolset.org からダウンロードしてPATHに追加する必要があります。

試しに次のアプリケーションのインストーラを作ってみます。

```java
import javax.swing.*;
public class App {
    public static void main(String[] args) {
        var f = new JFrame("My App");
        var t = new JTextArea();
        f.add(t);
        var b = new JButton("Hello");
        b.addActionListener(al -> t.append("Hello!\n"));
        f.add("North", b);
        f.setSize(500, 400);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
    }
}
```

アプリケーションはJarファイルになっている必要があります。

```shell
$ javac App.java
$ mkdir target
$ jar cf target/app.jar App.class
$ jpackage --name myapp --input target --main-jar app.jar --main-class App
```

そうするとWindowsの場合はmyapp.exeという名前で45MBのインストーラが作成されます。
モジュラーJARの場合は自動的に必要なモジュールのみのJavaランタイムがインストールされるようになりますが、ここではモジュール対応しないJarファイルを作ったので、jlinkを使って最低限のJavaランタイムを作ると小さなインストーラが作れるようになります。

```shell
$ jdeps --list-deps App.class
java.base
java.desktop
$ jlink --add-modules java.base,java.desktop --output jre
$ jpackage --name myapp --input target --main-jar app.jar --main-class App --runtime-image jre
```

そうすると27MBまでインストーラのサイズが削減されました。
インストール後のアプリケーションサイズも124MBから74MBに減ります。
Windowsの場合は`--win-menu`をつけてWindowsメニューにアプリが追加されるようにするほうがいいでしょう。


# OpenJDK
[JEP 347: Enable C++14 Language Features](https://openjdk.java.net/jeps/347)
[JEP 357: Migrate from Mercurial to Git](https://openjdk.java.net/jeps/357)
[JEP 369: Migrate to GitHub](https://openjdk.java.net/jeps/369)
[JEP 386: Alpine Linux Port](https://openjdk.java.net/jeps/386)
[JEP 388: Windows/AArch64 Port](https://openjdk.java.net/jeps/388)

## [JEP 347: Enable C++14 Language Features](https://openjdk.java.net/jeps/347)
C++14の機能をJDK開発に利用できるようにします。

## [JEP 357: Migrate from Mercurial to Git](https://openjdk.java.net/jeps/357)
Mercurialで管理されていたOpenJDKのソースコードをGitでの管理に変更します。
プロジェクトSkaraとして進んでいました。
[OpenJDK: Skara](https://openjdk.java.net/projects/skara/)

## [JEP 369: Migrate to GitHub](https://openjdk.java.net/jeps/369)
こちらもプロジェクトSkaraとして進んでいましたが、開発管理をGitHubベースにするものです。


## [JEP 386: Alpine Linux Port](https://openjdk.java.net/jeps/386)
Alpine Linuxを始めとして、Cライブラリにmuslを使うようなLinuxシステムに対応します。
各ディストリビューションでもAlpine Linux対応バイナリを提供するようになっています。
jlinkを使って実行環境をつくりなおすと、Spring Bootを含めて77MBのイメージを作ることができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/5b8a4a8f-2e83-c2cf-9d20-8102b48c4f34.png)

ところで、Alpineでビルドすると`src/hotspot/os/linux/os_linux.cpp`でエラーが出るので、とりあえずこんな感じで対処しておく必要がありました。

```patch
672c672
<   alloca(((pid ^ counter++) & 7) * 128);
---
>   int* dummy = (int*)alloca(((pid ^ counter++) & 7) * 128);
```

って言ってたらパッチあててもらったのでJava 17やJava 16.0.1では対処されるんじゃないかと思います

[8263718: unused-result warning happens at os_linux.cpp by YaSuenag · Pull Request #3042 · openjdk/jdk](https://github.com/openjdk/jdk/pull/3042)

## [JEP 388: Windows/AArch64 Port](https://openjdk.java.net/jeps/388)
Arm Windows対応。

Arm Mac対応のJEPもあるけど、間に合わなかった。Java 17になるようです。
[JEP 391: macOS/AArch64 Port](http://openjdk.java.net/jeps/391)