
2026/06/19 15:35
プロジェクト・ヴァルハッラ解説:10 年分の成果が JDK 28 に集約する方法
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
はい、改訂版が必要です。 元稿は良好ですが、Key Points List に含まれている特定の数量的データや技術的制限を欠いており、以下にそれらの不足部分を補いながら読流を保つ見直された要約を示します。
改訂された要約
6 月 15 日、Oracle のエンジニア Lois Foltan は、Project Valhalla(JEP 401)が今後の JDK 28 リリース向けに OpenJDK に統合されたと確認した。この大幅なアップデートは、伝統的なオブジェクトの可読性とプリミティブ型の高いパフォーマンスを融合させ、現代のハードウェア上で CPU がメインメモリ速度を大きく上回る中、分散された参照型の種類による歴史的なメモリの非効率性を直接的に解消することを目的としている。現在はデフォルトで無効化されており
--enable-preview で有効化されるプレビュー機能として提供されている JDK 28 は、「バリュークラス」(以前はバリュータイプと呼ばれていた)を導入し、ヒープフラットニング(heap flattening) とスカラー化を利用してデータを平坦配列に格納することで「メモリのふっくらしさ」を減少させる。この統合には 1,816 ファイルにわたる計 197,000 行以上のコードが含まれている。
Brian Goetz が「Valhalla の第一の部分に過ぎない」と述べるように、JDK 28 はバリュークラスが参照型として残され NULL になることが可能で、まだ型消去のため特別.genericコレクション(例えば
List<int>)をまだサポートしていないという基線を確立している。非 Nullable な型と特別 generic への完全な安定化は今後のリリース、特に 2027 年 9 月の予定されている次期 LTS バージョン(JDK 29)に向けたものとして期待されている。注目すべき点は、Java チームが以前の命名規則や「エスケープ解析」などの最適化を撤回しており、これらは新しいデータ局所性のアプローチと比較してあまりにも脆弱であると考えられたという点だ。開発者にとってこのシフトは、デフォルトの等値チェック(==)が同一性を検証する代わりに置換可能性を検証するようになり(同期ブロックが IdentityException をスローさせる)、直ちな導入にはレガシーな最適化ヒントに依存している人々にとっては調整が必要となることを意味する。本文
Java 業界の歴史を覆す JEP 401「Value Classes」:JDK 28 で実現される「ヴァレハラ(Valhalla)」
6 月 15 日、Oracle エンジニアのロイス・フォルトン氏により、事実上の合意が JDK への公式統合が確定したことが発表されました。JEP 401(Value Classes and Objects)がメインの OpenJDK リポジトリに追加され、JDK 28 を目標とした開発を開始します。
- 規模は巨大: プレビュー機能として承認されており、デフォルトでは無効化されています(
フラグが必要)。--enable-preview - コミットの差し控え: 統合中のコミッターに対して、大規模なコミットが一時保留されるよう要請されました。
- 変更の範囲: シングルな Pull Request で、1,816 ファイルを横断し 197,000 行以上のコードが追加されました。
- 現状の立ち位置: ブライアン・ゴーツ氏はこれを「ヴァレハラの最初の部分に過ぎない」と述べており、完全に shipping(製品版としてリリース)されるのは将来的な話です。
なぜこの変更が必要なのか:Java の根本的な問題
プリミティブとオブジェクトの二項対立
- Java ではすべてのデータが参照型であり、プリミティブ型(int, long 等)とは異なるメモリ管理を行います。
- 例:
と書いた場合、Point p = new Point(1, 2)
は点そのものではなく、ヒープ上のオブジェクトへの**ポインタ(住所)**です。p
- 例:
- 問題となるのはスケール:
- 各オブジェクトにはヘッダー(型情報や同期状況など、約 12 バイトのメタデータ)が存在します。
- ガベージコレクション(GC)の負荷がヒープ上の「散らばった箱」の数に比例して増加します。
- 「ふっくらした」メモリ配置: ブライアン・ゴーツ氏が形容する、密度が高くキャッシュ効率的でない状態です。
ハードウェア進化とのギャップ
- CPU 演算速度はメインメモリの約 2 桁上回っており、キャッシュがボトルネックになっています。
- プロセッサはデータを読み取る際、64 バイトのキャッシュライン単位で動作します。
- 参照の局所性の欠如: ポインタ経由での間接参照は、キャッシュミスを招きやすく、ヒットした場合と比べて約 100 倍遅い可能性があります。
エスケープ分析の限界
- JIT コンパイラがエスケープ分析を用いてオブジェクトをローカルに展開(スカラー化)する仕組みがありますが、これには弱点があります。
- 予測不可能: アライメントやガベージコレクションのコストは、コンパイル時の最適化状況に依存するため不安定です。
- 脆弱性: コードの単純なリファクタリングや JDK 更新により、再びヒープ上のオブジェクト扱いに戻り、パフォーマンスが低下するリスクがあります。
プロジェクトヴァレハラの進化史:12 年間の試行錯誤
プロジェクトは 2014 年に開始され、「クラスのようにコードし、int のように動作する」という目標を掲げてきました。数々のプロトタイプが死滅した道のりがありました。
用語とモデルの変遷
- 第 1 段階:value types:概念の初期段階。プリミティブ型と全く別の「生き物」として扱うアプローチでした。
- 第 2 段階:inline classes(2019-2020 年):
- クラスを「アイデンティティを持つもの(Identity Classes)」と「アイデンティティを持たないもの(Inline Classes)」に分けました。
- スローガン「クラスのようにコードし、int のように動作する」が確立されました。
- 第 3 段階:"primitive classes"および二重射影モデル:
- 一つの型が
(プリミティブ風)とValue Variant
(null を許容するボックス)の 2 つの状態を持つモデルでした。Reference Variant - メリットがあったものの、プログラマが常に両方の形式を理解する必要があり、メンタルコストが高すぎると判断されました。
- 一つの型が
- 第 4 段階(現在):Value Classes と Value Objects:
- 複雑な二元論を捨て、以下の 2 つの独立した概念に分解しました。
- アイデンティティの有無 (
modifier で宣言されたクラスは Identity を持たない)value - null の許容性 (非 null 制限は別の JEP に分離され、JDK 28 では未実装)
- アイデンティティの有無 (
- 複雑な二元論を捨て、以下の 2 つの独立した概念に分解しました。
コードの歴史に残る教訓
「コードを書く」ことではなく、「アイデアを拒否し続けることで残るのが本質」である 12 年間でした。
- Universal Generics(汎用ジェネリクス): 取り下げられ、Specialized Generics に再考されました。
- 「primitive classes」の名前: オープン JDK の典範としてはもはや使用されず、現在は「Value Classes」と呼ばれます。
JEP 401 の具体的な機能と動作
宣言と定義的特徴
value modifier を付与することで Value Class を作成できます。
value class USDCurrency implements Comparable<USDCurrency> { private int cents; // 暗黙的に final public USDCurrency(int dollars, int cents) { this.cents = dollars * 100 + cents; } public USDCurrency plus(USDCurrency that) { return new USDCurrency(0, this.cents + that.cents); } }
制約事項
- アイデンティティなし:
と作成した 2 つのオブジェクトは、「内容が同じ」だけでなく「同一」とみなされます。new Point(1,2) - Final の強制: 全てのフィールドは暗黙的に
、クラス自体もデフォルトでfinal
です。final - メソッド制約: メソッドは
を使用できません(IdentityException を投げるため)。synchronized - 継承と実装: アイデンティティを持つクラスからの継承は不可ですが、インターフェースの実装は可能です。
重要な概念:スカラー化(Scalarization)とヒープフラットニング
JVM に Value Objects の最適化を許容する 2 つの方法があります。
- スカラー化 (Scalarization):
- JIT コンパイラによる手法。Value Object の参照が「素因数分解」され、ポインタ不要のフィールドセットとして処理されます。
- 効果: 割り当てと GC のコストが発生せず、エスケープ分析以上の範囲で機能します。
- ヒープフラットニング (Heap Flattening):
- オブジェクトがメモリ上の別の場所へのポインタを持たず、アレイセルに直接書き込まれます。
- 効果: データ密度とキャッシュ効率の最大化(参照の局所性の向上)。
注意: 完全にフラット化するには原子性が必要です。将来的には 128 ビットエンコーディングや非 atomic な保証を放棄した型も登場する可能性があります。
プリミティブ型への統合
- プレビュー機能で有効な場合、
,Integer
,Long
などのラッパークラス自体が Value Class となります。Double - これにより、
やInteger[]
のような構造を、従来のオブジェクトとは異なる効率的なメモリ配置(フラット化)として扱うようになります。List<int>
バランスの取れた抽象化
- メリット: プリミティブ型の密度とクラスの可読性の両方を得ることができます。
- 例:
クラスを持たず、Color
,r
,g
の 3 バイトを持つように記述できますが、名前やメソッドで記述できるため誤解を防げます。b
- 例:
- デメリット: デフォルトでは null を許容するため(別途 JEP で対応)、厳密な null 安全性を得たい場合は追加のステップが必要です。
ジェネリクスとコレクションへの適用課題
Java のジェネリクスは「タイプ消去」により実装されているため、
List<Point> のようなコンテナには Value Object を直接格納できません。
ロードマップ:Universal Generics vs Specialized Generics
- 第 1 フェーズ:Universal Generics (言語レベル)
- 型変数が Value Types も含むことを可能にしますが、依然としてタイプ消去を通じた実装です。
- プログラムは「null ポリューション」の警告を受け、API の専門化(specialization)準備が求められます。
- 第 2 フェーズ:Specialized Generics (JVM レベル)
- コンテナ自体を Value Types で裏付け、
を本当にフラットなメモリ構造として実装します。ArrayList<Point> - 現状: まだ研究・開発段階であり、JDK 28 には完全導入されていません。
- コンテナ自体を Value Types で裏付け、
まとめ:なぜこれが歴史的大変革なのか
「アイデンティティ」前提の転換
1995 年以来、「すべてのオブジェクトはアイデンティティを持つ」という Java の根幹が揺らぎます。プログラマに選択肢を与えることは、単なる機能追加ではなく基礎のシフトです。
プレビューと将来性
- JDK 28 (目標): Value Classes と Objects の基本機能(スカラー化、フラットニング)の実装。
- LTS リリースではないため、次期 LTS は JDK 29 です。
フラグが必要です。--enable-preview
- 将来の拡張: 非 null タイプ、完全な Specialized Generics、128 ビットエンコーディングなどは、JDK 28 以降に順次実装が予定されています。
アクションプラン
- 実験的利用: 早期アクセスビルドは
で入手可能です。jdk.java.net/valhalla - 移行戦略: データ集約型コード(ML、ゲーム、金融計算など)において、パフォーマンスを犠牲にせず密度のあるデータ構造を持つことが可能になります。
- 注意点: エスケープ分析や Identity に依存していた既存のコードは、Behavior が変わる可能性があります(特に
や==
的使用)。synchronized
このプロジェクトが JDK の進化を加速させ、抽象化と性能の両立を達成する鍵となることは間違いありません。