Java 14新機能まとめ

Java 14が2020/3/17にリリースされました。
[Java SE 14 Platform JSR 389](https://openjdk.java.net/projects/jdk/14/spec/)

# ダウンロード

OpenJDKサイトからダウンロードできます。
https://jdk.java.net/14/

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

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

- [AdoptOpenJDK](https://adoptopenjdk.net/?variant=openjdk14&jvmVariant=hotspot)
- [Azul Zulu Community](https://jp.azul.com/downloads/zulu-community/?version=java-14&architecture=x86-64-bit&package=jdk)
- [Liberica JDK](https://bell-sw.com/pages/java-14/)

LTSではないのでAmazon Correttoではリリースされないようです。
アップデートは4月に14.0.1が、7月に14.0.2がリリースされることになります。

Oracle JDKは開発用途には利用できますが、商用利用にはJava SE Subscriptionを購入する必要があります。

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

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

今回は、16ものJEPが取り込まれました。影響が大きいものも多いです。
305: [Pattern Matching for instanceof (Preview)](https://openjdk.java.net/jeps/305)
343: [Packaging Tool (Incubator)](https://openjdk.java.net/jeps/343)
345: [NUMA-Aware Memory Allocation for G1](https://openjdk.java.net/jeps/345)
349: [JFR Event Streaming](https://openjdk.java.net/jeps/349)
352: [Non-Volatile Mapped Byte Buffers](https://openjdk.java.net/jeps/352)
358: [Helpful NullPointerExceptions](https://openjdk.java.net/jeps/358)
359: [Records (Preview)](https://openjdk.java.net/jeps/359)
361: [Switch Expressions (Standard)](https://openjdk.java.net/jeps/361)
362: [Deprecate the Solaris and SPARC Ports](https://openjdk.java.net/jeps/362)
363: [Remove the Concurrent Mark Sweep (CMS) Garbage Collector](https://openjdk.java.net/jeps/363)
364: [ZGC on macOS](https://openjdk.java.net/jeps/364)
365: [ZGC on Windows](https://openjdk.java.net/jeps/365)
366: [Deprecate the ParallelScavenge + SerialOld GC Combination](https://openjdk.java.net/jeps/366)
367: [Remove the Pack200 Tools and API](https://openjdk.java.net/jeps/367)
368: [Text Blocks (Second Preview)](https://openjdk.java.net/jeps/368)
370: [Foreign-Memory Access API (Incubator)](https://openjdk.java.net/jeps/370)

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

## 言語仕様
言語仕様にかかわる変更としては次のようなものがあります。
359: [Records (Preview)](https://openjdk.java.net/jeps/359)
305: [Pattern Matching for instanceof (Preview)](https://openjdk.java.net/jeps/305)
368: [Text Blocks (Second Preview)](https://openjdk.java.net/jeps/368)
361: [Switch Expressions (Standard)](https://openjdk.java.net/jeps/361)

### 359: [Records (Preview)](https://openjdk.java.net/jeps/359)

データ保持用のクラスとしてrecordがpreview機能として入りました。
Java 15でフィードバックを反映した改善版が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) {
  public Range {
    if (lo > hi) throw IllegalArgumentException();
    // 設定されなかったフィールドはあとで設定される
  }
}
```

staticではないinner classの中でrecordを定義することはできません。
次のようなコードをコンパイルすると`record declarations not allowed in inner classes`というエラーになります。

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

次のようなコードはコンパイルできます。

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

#### 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`という名前を型(クラス・インタフェース・レコード型など)につけることは制限されています。
`--enable-preview`を付けた状態ではエラーになります。

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

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

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

jshell> record Rec(int record){}

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

ちなみにこの「リリース13から」というのは誤りで、ただしくは14からです。
http://mail.openjdk.java.net/pipermail/amber-dev/2020-February/005623.html

enumと違いキーワードではないので、変数名やフィールド、recordのコンポーネント名にはrecordを使えます。
`--enable-preview`を付けない状態では、警告が表示されます。

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

jshell> class record{}
|  警告:
|  'record'は将来のリリースで制限された型名になる可能性があり、型の宣言での使用、または配列の要素タイプとしての使用はできなくなる可能性があります
|  class record{}
|        ^
|  次を作成しました: クラス record
```

#### 今後の改善

当初はSealed Typesと同じJEPでしたが分離されました。
クラスの継承を限られたクラスに制限できる機能です。Java 15でpreviewに入ると思われます。
[JEP 360: Sealed Types (Preview)](http://openjdk.java.net/jeps/360)

Preview 2での改善点はこちらで提案されています。
https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001913.html

- `java.util.Record`はクラスですが、Valhallaのinline classはinterfaceしか継承できないので、これをinterfaceにしたほうがいいかどうか。
- 必須メンバーへの可視性(private recordのメンバがpublicなのはおかしい？)
- 現状ではstaticではないinner classではrecordを入れ子にすることができないけど対応したい
- abstract record
- パターンマッチでのdeconstructionへの対応
- recordコンポーネントへの`@Deprecated`

参考
Stephen Colebourneさんからのフィードバック
https://mail.openjdk.java.net/pipermail/amber-dev/2019-November/005271.html

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

パターンマッチングです。
まずはinstanceofを使ったパターンマッチが14にプレビューとして入ります。
http://openjdk.java.net/jeps/305

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

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

switchで使えるようになれば便利ですが、これは別のJEPで定義されていて、Java 15に持ち越されています。
[JEP draft: Pattern matching for switch (Preview)](http://openjdk.java.net/jeps/8213076)

また、recordを分解する機能(deconstruction)やその入れ子が導入されてPreview 2としてJava 15に入るようです。
http://openjdk.java.net/jeps/8235186

妥当なスケジュールとしては

- 14 Pattern Matching for instanceof(Preview)
- 15 Pattern Matching for instanceof(Preview 2)  
Pattern matching for a switch (Preview)
- 16 Pattern Matching for instanceof(Standard)  
Pattern matching for a switch (Preview 2)
- 17LTS Pattern matching for a switch(Standard)

となるか

- 14 Pattern Matching for instanceof(Preview)
- 15 Pattern Matching for instanceof(Preview 2)  
Pattern matching for a switch (Preview)
- 16 Pattern Matching for instanceof(Preview 3)  
Pattern matching for a switch (Preview 2)
- 17LTS Pattern Matching for instanceof(Standard)  
Pattern matching for a switch(Standard)

となるかですが、17LTSにはパターンマッチングの機能がひととおり標準機能として入る目途がついてきました。


### 368: [Text Blocks (Second Preview)](https://openjdk.java.net/jeps/368)

改行などを含んだ文字列を定義できます。`"""`で囲みます。
JDK13にPreviewとして入ったものにJDK14では改行のエスケープなど少し仕様変更が入りました。JDK15でstandardになる予定。

```java
// same as "You can write\ntwo line string.\n"
var str = """
  You can write
  two line string.
  """;
```

開始の`"""`のあとには文字列を続けれません。また、インデントは`"""`や内部の文字列で一番浅いところが基準になります。

```java
var str = """
..You can write
..two line string.
  """;
```

```java
var str = """
..  You can write
..  two line string.
  """;
```

改行をエスケープすることもできます。

```java
var str = """
  You can write \
  two line string, \
  but this is single.
  """;
```

これは`"You can write two line string, but this is single."`になります。

行末のスペースは削除されます。
そこで、行末にスペースが必要なときは`\s`を入れてスペースが必要なことを示します。

```java
var str = """
  test\s
  test \s
  """;
```

これは`"test_\ntest__\n"`になります。(Qiitaでは複数スペースをいれてもスペースひとつになってしまう)

文字列への変数の埋め込みはできません。その代わりに`formatted`メソッドがインスタンスメソッドとして用意されて、次のように書けるようになりました。

```java
var str = """
  こんにちは、%sさん。
  今日はいい天気ですね。
  """.formatted("きしだ");
```

### 361: [Switch Expressions (Standard)](https://openjdk.java.net/jeps/361)

いままでステートメントであった`switch`を式として使えるようになります。Java 12でプレビューとして導入され、Java 13で仕様変更、そしてJava 14で正式機能として導入されます。
https://openjdk.java.net/jeps/361

`switch`はステートメントでしたが、多くのswitchで同一の変数に値を割り当てたりすべてのcaseでreturnしたりといった使いかたがされていたため、効率的に書けるように式としても使えるようになります。

つまり、こう。

```java
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};
```

`yield`で値を返すということもできます。

```java
int result = switch (s) {
    case "Foo":
        yield 1;
    case "Bar":
        yield 2;
    default:
        System.out.println("Neither Foo nor Bar, hmmm...");
        yield 3;
}
```

基本的な形は`yield`で値を返すものです。

```java
case LABEL: yield expression;
```

そのシンタックスシュガーとしてラムダっぽく書けるということのようです。

```java
case LABEL -> expression;
```

また、`case`に複数の値を指定できるようにもなります。

```java
switch (day) {
    case MONDAY, FRIDAY, SUNDAY: 
        numLetters = 6;
        break;
    ...
};
```

このような`case`の拡張は、既存の`switch`ステートメントでも有効です。


## JVM
JVMの挙動変更としては、`NullPointerException`での詳細メッセージがあります。
358: [Helpful NullPointerExceptions](https://openjdk.java.net/jeps/358)

### 358: [Helpful NullPointerExceptions](https://openjdk.java.net/jeps/358)

Javaプログラマのみなさんは`NullPointerException`が大好きだと思います。
しかし、メッセージがそっけないという悩みがありました。
Java 14では、`NullPointerException`について詳細なメッセージを表示できるようになりました。

例えば次のようなコードがあるとします。

```java
public class Sample {
  static String s;
  public static void main(String... args) {
    s.length();
  }
}
```

Java 14でも普通に実行すると以前のバージョンと同様に特別なメッセージは表示されません。

```
$ java Sample.java
Exception in thread "main" java.lang.NullPointerException
        at Sample.main(mysample.java:4)
```

`-XX:+ShowCodeDetailsInExceptionMessages`オプションをつけて実行すると次のように何に対して何を呼び出したときにエラーになったかということが表示されます。

```
$ java -XX:+ShowCodeDetailsInExceptionMessages Sample.java
Exception in thread "main" java.lang.NullPointerException:
    Cannot invoke "String.length()" because "Sample.s" is null
        at Sample.main(mysample.java:4)
```

このメッセージ構築はメッセージ取得時に行われるので、パフォーマンスへの影響はほとんどないようです。

JShellの場合は`-R-XX:+ShowCodeDetailsInExceptionMessages`をつけます。

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

jshell> String[] strs = {null}
strs ==> String[1] { null }

jshell> strs[0].toUpperCase()
|  例外java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because "REPL.$JShell$11.strs[0]" is null
|        at (#2:1)
```

## API
APIに関する変更は次の3つのJEPです。
349: [JFR Event Streaming](https://openjdk.java.net/jeps/349)
370: [Foreign-Memory Access API (Incubator)](https://openjdk.java.net/jeps/370)
352: [Non-Volatile Mapped Byte Buffers](https://openjdk.java.net/jeps/352)
この他に`CompactNumberFormat`での複数形対応とStrictMathでの便利メソッドの追加がありました。
また、あとでツールの項で取り上げるPackagingツール関連のAPIがIncubatorとして追加されたのと、Pac200関連のAPIが削除されています。

APIの相違はこちらでまとまっています。
[DRAFT: API Differences Between Java SE 13 (build 33) & Java SE 14 (build 36)](https://cr.openjdk.java.net/~iris/se/14/latestSpec/apidiffs/overview-summary.html)

### 349: [JFR Event Streaming](https://openjdk.java.net/jeps/349)

Flight Recorderはメトリクス収集ツールで、障害発生時の解析に役立ちますが、ダンプファイルの解析が必要で、モニタリング用途には不便でした。
JFR Event Streamingでは、イベント登録ができるなど、モニタリングに使いやすい機能が追加されています。

JEPのサンプルではイベントの登録の例が載っていますが、これにGCログの出力をつけくわえてみました。

``` java
import java.io.IOException;
import java.time.Duration;
import jdk.jfr.consumer.RecordingStream;
public class JFRStreamTest {
     
    public static void main(String[] args) throws IOException  {
         
        try (var rs = new RecordingStream()) {
            rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
            rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
            rs.onEvent("jdk.CPULoad", event -> {
                System.out.println(event.getFloat("machineTotal"));
            });
            rs.onEvent("jdk.JavaMonitorEnter", event -> {
                System.out.println(event.getClass("monitorClass"));
            });
            rs.onEvent("jdk.GarbageCollection", System.out::println);
            rs.start();
        }
    }
}
```

`-XX:StartFilghtRecording`をつけて実行すると、Flight Recorderが起動して、定期的にメトリクスが表示されます。

```
$ java -XX:StartFlightRecording JFRStreamTest.java
Started recording 1. No limit specified, using maxsize=250MB as default.

Use jcmd 79660 JFR.dump name=1 filename=FILEPATH to copy recording data to file.
[1.715s][warning][os] Unable to resolve PDH index: (230)
[1.716s][warning][os] Please check the registry if this performance object/counter is disabled
{
  classLoader = null  name = "jdk/jfr/internal/PlatformRecorder"
  package = {
    name = "jdk/jfr/internal"
    module = {
      name = "jdk.jfr"
      version = "14-ea"
      location = "jrt:/jdk.jfr"
      classLoader = null    }
    exported = true
  }
  modifiers = 49
}

jdk.GarbageCollection {
  startTime = 13:51:48.973
  duration = 12.5 ms
  gcId = 1
  name = "G1New"
  cause = "G1 Evacuation Pause"
  sumOfPauses = 12.5 ms
  longestPause = 12.5 ms
}
```

[JDK Mission Control(JMC)](https://jdk.java.net/jmc/)で見ることができます。
JFRを動かしておくと、イベントブラウザにイベントが記録されているのがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/a7d87087-c7a2-2a26-5cf6-cda9971b27da.png)


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

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

ということで、ヒープ外のメモリを直接扱うAPIが導入されたわけです。
次のようなコードになります。

```java
VarHandle intHandle = MemoryHandles.varHandle(int.class);

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

### 352: [Non-Volatile Mapped Byte Buffers](https://openjdk.java.net/jeps/352)

ByteBufferを不揮発メモリに対応します。
`jdk.nio.mapmode`といモジュールが導入されて、同名のパッケージに`ExtendedMapMode`クラスが用意され、新しいMapModeとして

- `READ_ONLY_SYNC`
- `READ_WRITE_SYNC`

が追加されます。

```
var fc = FileChannel.open(path);
fc.map(ExtendedMapMode.READ_WRITE_SYNC, 0, 1024);
```

などとする感じです。デバイスが不揮発メモリではない場合はUnsupportedOperationExceptionが投げられます。

また、BufferPoolMXBeanに永続MappedByteBufferの統計情報が`mapped - 'non-volatile memory'`という名前で取れるようになるようです。

### @PreviewFeature

String.formattedのようなプレビュー機能のために導入APIには、13ではDeprecatedがついていました。
14ではinternalなアノテーションとして`@PreviewFeature`が導入されて、プレビュー機能用のAPIであることが明示されました。
13では`--enable-preview`がなくても使えましたが、14では`--enable-preview`の指定がないときにこれらのAPIを使うと

```
formatted(java.lang.Object...)はプレビュー機能の一部であるAPIです
```
のようなエラーが出ます。

### シリアライズ関連メソッド・フィールドを@Serialで明示

シリアライズできるクラスには関連フィールドやメソッドを定義しますが、シグネチャが決まっているもののコンパイル時にチェックされず、正しく定義されているかどうか確認できませんでした。


private static final long serialVersionUID

https://download.java.net/java/GA/jdk14/docs/api/java.base/java/io/Serial.html
https://bugs.openjdk.java.net/browse/JDK-8217698

### CompactNumberFormatの複数形対応

JEPになってない変更のひとつ。
ドイツ語とかイタリア語の場合に100万と200万でMillionかMillionenみたいな変形があって、それに対応したということらしい。

```bash
jshell> import java.text.*

jshell> var cnf = CompactNumberFormat.getCompactNumberInstance(Locale.GERMAN, NumberFormat.Style.LONG)
cnf ==> java.text.CompactNumberFormat@5088aaba

jshell> cnf.format(1_000_000)
$3 ==> "1 Million"

jshell> cnf.format(2_000_000)
$4 ==> "2 Millionen"

```

Unicodeの[Language Plural Rules](https://unicode.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules)に従って独自のルールを設定することができるコンストラクタも用意されています。

### StrictMathへの便利メソッド追加

StrictMathに便利メソッドが追加されています。メソッド参照が使いやすくなることを狙ったんでしょうか。

- incrementExact(int)
- incrementExact(long)
- decrementExact(int)
- decrementExact(long)
- negateExact(int)
- negateExact(long)

インクリメント、デクリメント、符号反転ですが、オーバーフローしたときに
エラーがでます。

```
jshell> StrictMath.incrementExact(3)
$5 ==> 4

jshell> StrictMath.incrementExact(Integer.MAX_VALUE)
|  例外java.lang.ArithmeticException: integer overflow
|        at Math.incrementExact (Math.java:968)
|        at StrictMath.incrementExact (StrictMath.java:849)
|        at (#4:1)

jshell> StrictMath.negateExact(Integer.MAX_VALUE)
$6 ==> -2147483647

jshell> StrictMath.negateExact(Integer.MIN_VALUE)
|  例外java.lang.ArithmeticException: integer overflow
|        at Math.negateExact (Math.java:1044)
|        at StrictMath.negateExact (StrictMath.java:909)
|        at (#7:1)
```

通常の演算子ではオーバーフローがおきます。

```
jshell> Integer.MAX_VALUE+1
$8 ==> -2147483648

jshell> Integer.MIN_VALUE
$9 ==> -2147483648

jshell> -Integer.MIN_VALUE
$10 ==> -2147483648
```


## ツール
ツールの変更としては、jpackageの追加が大きいですね。
343: [Packaging Tool (Incubator)](https://openjdk.java.net/jeps/343)
367: [Remove the Pack200 Tools and API](https://openjdk.java.net/jeps/367)

### 343: [Packaging Tool (Incubator)](https://openjdk.java.net/jeps/343)

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メニューにアプリが追加されるようにするほうがいいでしょう。

### 367: [Remove the Pack200 Tools and API](https://openjdk.java.net/jeps/367)

Pack200はJarファイルを効率的に圧縮するツールでしたが、主なユースケースであるAppletが使われなくなり、役目を終わったということで削除されます。
`java.util.jar`配下の関連APIも削除されます。

## GC
GCに関する変更のJEPは5つありました。
363: [Remove the Concurrent Mark Sweep (CMS) Garbage Collector](https://openjdk.java.net/jeps/363)
364: [ZGC on macOS](https://openjdk.java.net/jeps/364)
365: [ZGC on Windows](https://openjdk.java.net/jeps/365)
366: [Deprecate the ParallelScavenge + SerialOld GC Combination](https://openjdk.java.net/jeps/366)
345: [NUMA-Aware Memory Allocation for G1](https://openjdk.java.net/jeps/345)

### 363: [Remove the Concurrent Mark Sweep (CMS) Garbage Collector](https://openjdk.java.net/jeps/363)
Concurrent Mark & Sweep GCがソースコードから削除されます。
Java 9の[JEP 291](https://openjdk.java.net/jeps/291)でDeprecatedになっていましたが、メンテナンスを引き継ぐような人も現れなかったので削除するということです。
そうすることで、GCのメンテナンスコストが下がり、他のGCの開発が速くなることが期待されています。

### 364: [ZGC on macOS](https://openjdk.java.net/jeps/364) / 365: [ZGC on Windows](https://openjdk.java.net/jeps/365)

いままではLinuxしかサポートしてなかったZGCがmacOSとWindowsをサポートするようになりました。
当初は、ZGCは本番のLinuxサーバーでだけ使われて、macOSやWindowsは開発用途に使うだけだという想定でしたが、IDEをZGCで動かしたいなどの要望もあったことからmacOSやWindowsもサポートすることになったようです。
WindowsはWindows 10 ver 1803以降に対応しています。
ZGCは仮想アドレスを使って物理メモリを複数のアドレスでアクセスする仕組みを使っていますが、Windowsでは1803からページングファイルメモリがサポートされたということでWindowsでもZGCが動かせるようになったようです。

### 366: [Deprecate the ParallelScavenge + SerialOld GC Combination](https://openjdk.java.net/jeps/366)

Young領域のGCをPrallelでOld領域のGCをSerialでという、ほとんど使われてない割にメンテナンスが大変な組み合わせが非推奨になりました。

### 345: [NUMA-Aware Memory Allocation for G1](https://openjdk.java.net/jeps/345)
最近は各コアからのメモリアクセスが均等というわけではないNUMA(Non-Uniform Memory Access)アーキテクチャが広まっています。
パラレルGCではNUMAに対応していましたが、G1は対応していませんでした。
このJEPによってG1もNUMAに対応しています。ただし対応OSはLinuxのみです。

## JDK
JDK自体に関する変更というのは、リリース形態やビルド方針などに関するものです。
Java 14ではSolarisやSPARCへの対応がDeprecatedになりました。
362: [Deprecate the Solaris and SPARC Ports](https://openjdk.java.net/jeps/362)

### 362: [Deprecate the Solaris and SPARC Ports](https://openjdk.java.net/jeps/362)
Solaris/SPARCやSolaris/x64、Linux/SPARCへの対応を外していくというものです。
CMSの削除と同様に、もし、いっぱい需要があるということを示せれば、取り下げられます。
また、これはオープンソースとしてのOpenJDKの開発の話で、Oracle JDKなど商用ディストリビューションでの対応継続は、それぞれに続く可能性があります。Solaris/SPARCを導入してるところだとJavaのライセンス代もそれほど問題にならなそうだし。