- JVMの内部知識を小さな単位に分けて扱う進行中のミニポストシリーズで、各記事は1つのテーマ・テスト・ベンチマーク・観察に集中している
- 各ポストは5〜10分で読める分量を目標としており、JVMの要素同士が相互に作用することを前提としている
- 根拠や議論は逸話的である場合があり、誤り・一貫性・文体・文法・重複の見直しが十分でない可能性があるため、そのまま信頼するには注意が必要
- 全体のまとめはePUB, MOBI, PDFで提供され、PDFは高品質変換のため容量が数十MB級である
- 個別記事の索引はCompiler、Runtime、GC、Libraryの軸に分かれており、ロック最適化、TLAB、GC停止、
String.intern()、safepoint、圧縮参照、条件付き移動のようなJVM内部トピックを扱う
シリーズを読む際の前提
- JVM Anatomy Quarksは、JVMの基本知識を短い記事単位で整理する進行中のミニポストシリーズである
- 各ポストは1つのテーマ、テスト、ベンチマーク、観察を深く掘り下げる形式である
- 単一のポストだけを切り離して見ると文脈が不足することがあり、扱う要素の大半は相互に作用しやすい
- 記事の根拠や議論は逸話的である場合があり、誤り、一貫性、文体、文法、意味、重複の見直しが十分でない可能性がある
- 内容を利用または信頼する際は、読者自身がリスクを負う必要がある
まとめファイルと索引構造
- シリーズ全体のまとめは3つの形式で提供される
- ePUBは最も小さく1MB未満で、PandocのHTML-to-ePUBベース
- MOBIは小さく数MB級で、KindleGenのePUB-to-MOBIベース
- PDFは数十MB級で非常に大きく、wkhtmltopdfのHTML-to-PDFベースの高品質出力
- 個別索引はCompiler, Runtime, GC, Library分類で構成される
テーマ別記事一覧
-
Compiler中心の項目
#1: Lock Coarsening and Loops
#14: Constant Variables
#15: Just-In-Time Constants
#16: Megamorphic Virtual Calls
#17: Trust Non-Static Final Fields
#18: Scalar Replacement
#19: Lock Elision
#20: FPU Spills
#25: Implicit Null Checks
#27: Compiler Blackholes
#28: Frequency-Based Code Layout
#29: Uncommon Traps
#30: Conditional Moves
-
RuntimeとGCの両方にまたがる項目
- JVM実行中のメモリと停止動作を扱う記事群である
#2: Transparent Huge Pages
#4: TLAB Allocation
#5: TLABs and Heap Parsability
#6: New Object Stages
#7: Object Initialization Costs
#9: JNI Critical and GC Locker
#22: Safepoint Polls
-
GC中心の項目
- コレクタ設計とヒープ動作を中心に扱う
#3: GC Design and Pauses
#11: Moving GC and Locality
#13: Intergenerational Barriers
#21: Heap Uncommit
-
Runtime中心の項目
- JVM実行環境とオブジェクト表現を扱う記事群である
#12: Native Memory Tracking
#23: Compressed References
#24: Object Alignment
#26: Identity Hash Code
-
Libraryまたは複合分類の項目
- Libraryまで含まれる項目として
#10: String.intern()がある
- CompilerとRuntimeが併記される項目として
#16、#25、#29、#30がある
1件のコメント
Hacker Newsのコメント
https://shipilev.net/jvm/anatomy-quarks/17-trust-nonstatic-f... は本当に残念な事例
一部のフレームワークが JNI とリフレクションを乱用し、本来は不変であるべき
finalフィールドを書き換えてきたため、ユーザーコードはシステム提供クラスでのみ可能な 重要な最適化 を享受できていないプラットフォーム、特にコンパイラとランタイムは、将来の最適化の余地を守るために 意味論的制約 を非常に厳格に強制すべき
実際に
finalを変更しなければならないコードは多くなく、今でもその操作は自分のモジュール内のクラス、または明示的にopenされたクラスに制限されているので、今後はfinalを変更しようとするモジュールに対してアプリケーションが権限を付与する方式になる見込み最近ネイティブ呼び出しや安全でないメモリアクセスに適用した方式と似ている
[1]: https://openjdk.org/jeps/8305968
finalを付けろ という原罪を作った面もあるのではないかと思うそのせいで今ではテストで
finalクラスを簡単にモックできず、モックツールはfinalクラスをモックするために バイトコード操作 までしなければならない例えば Google 社内では Effective Java が要件で、公開 GDrive API にも
finalクラスがあるが、外部 API こそモックしたい対象System.outの事例は Java 自体の問題https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.htm...
こうした前例があるのだから、他の人たちも許容されるやり方だと考えたとしても不思議ではない
適切な回避手順を踏めば、これを回避できるべき
重要なのは安全性で、変更不可能なものを書き換えて SEGV を引き起こせてしまう可能性があるからであり、アクセス制御子が対処しようとしている懸念もまさにそこ
finalのように戻す 邪悪な3行コード を自分で書いたことがあると認めるできないように塞がれていてほしかったが、ビジネス上の要件があった
少し話は変わるが、Apple が Swift Java ブリッジ を新たに公開し、かなりクール
JNI と Panama の両方をサポートしており、先週はこれを Android に移植していた
https://github.com/swiftlang/swift-java
言語間の相互運用性のおかげで、適切な場合には1つのライブラリを複数のプラットフォームで共有できる
これまでは C++ でのみ、必要なら C API を付ける形でやってきたが、C++ 自体が不要な状況では、当然ながら第一選択の言語ではない
ただしコストは気になる。モバイルアプリで Swift が Kotlin ライブラリを簡単に呼び出せるなら、iOS アプリは何らかの形で JVM のようなものをロードしなければならないのではないかと思うし、逆に Android アプリで Swift を呼ぶなら Swift ランタイムをロードすることになりそう
結局オーバーヘッドが発生する
いつか開発者が Swift ライブラリに依存し、そのライブラリが Kotlin ライブラリに依存して JVM を起動し、さらに JNI で C++ を呼び出す、という構成が当たり前になるのではと心配している
これは現代のパッケージマネージャのように、直接依存は数個しかなくても、「簡単すぎて気にしなかった」結果、プログラムがあっという間に 100個以上の推移的依存関係 を抱える状況に似ている
この良い記事シリーズがここで共有されてうれしい。このシリーズを見て JVM について多くを学んだ
特に、Java でよく「スタック割り当て」と呼ばれる表現が誤りだとするこの記事が気に入っている: https://shipilev.net/jvm/anatomy-quarks/18-scalar-replacemen...
JVM が実際にやっているのは エスケープ解析 + スカラ置換
これらの記事の 分量 が気に入っている
数分で1本をさっと読めるし、その気になればベンチマークをローカルで回してみることもできる
JVM ベースの言語で数年働いたことがあるなら、この記事シリーズは本当に興味深い
数年前に初めて読み進めたときのことを覚えている
このシリーズ名がなぜ『JVM Anatomy Park』から変わったのか知っている人はいる?
Javaはほとんど忘れて過ごすようになった。
新しいプロジェクトをJavaで始めようとはまったく思わない。
迅速な開発と柔軟性が必要ならPython、ガベージコレクションのある方式で大量のI/O並行性を扱いたいならGo、コンパイル言語でバランスの取れた良い言語が必要ならSwift、性能と安全性が必要なコンパイル言語ならRustを選ぶと思う。
個人的な好みにすぎないし、KotlinがJavaをより使いやすくしたことは分かっているが、それでもそう感じる。
Javaの主な強みは、Java経験のある開発者がほぼ無限にいて、既存ライブラリが膨大で、その多くがエンタープライズ志向であり、多くの貢献者がいる非常に大きなコードベースを管理しやすく、数十年にわたって開発されてきた標準VMが非常に堅牢でかなり高速で、事実上あらゆるプラットフォームでサポートされている点だ。
2000年代初頭ほど、さらにはエンタープライズでさえ、圧倒的な支配力を持っているわけではなく、典型的な「blub」言語ではあるが、エンタープライズ規模を見込んでいて、純粋な性能よりも多数の開発者へ拡張できることのほうが重要なら、十分に合理的な選択だ。
Rustは好きだが、生活費を稼がせてくれるのはJavaだ。
モダンなフレームワークとAI支援のおかげで、そこそこのバックエンドを数日で立ち上げられる。
1人の技術系共同創業者なら、MVPを素早く作るためにJavaかKotlin、それにフロントエンドスタック程度を知っていればよく、実際にはコーディング以外の作業にずっと多くの時間を使うので、言語機能の差はそれほど重要ではなくなる。
モバイルネイティブに行くなら、Swiftが第2の言語になるかもしれない。
また、しばらくの間はスケーラビリティが最初の問題にならない可能性が高い。先に問題になるのはチームの拡大で、性能ボトルネックが目立つのはずっと後になる可能性が高い。
Javaは大きなチームに向いている。
ビジネスの観点から、より大きな人材プール、速いデリバリーサイクル、長期的に中核スタックとして残せるものを望むなら、JavaかKotlinがおそらく最善だ。
特定の開発者層を引きつけるために魅力的な技術を福利厚生のように打ち出したい、あるいは珍しいビジネス事例があるなら、GoやRustを選ぶこともできる。
Pythonは学界やブートキャンプで人気だが、汎用バックエンドにおけるビジネス価値は正直よく分からない。
JVM全体を無視することはしない。JVMは工学的傑作であり、最近も急速に進化している。Loom、Panama、Leydenを見れば分かる。
だから、ほとんどあらゆるものに対してJavaはかなり明確に良い選択だ。
Java 21+と組み合わせたKotlinは、I/O中心のサービスでも、事実上どんなサービスでも、私の第一選択の組み合わせだ。
本当に使いやすく、仮想スレッドのおかげでGoと同じくらいシンプルで効率的なコードが書けるうえ、世界最大級で質も高いライブラリエコシステムを活用できる。
GoやPythonをけなすつもりはない。好みのツールなら十分に良い。
ただ、Javaは思っているほど無関係な存在にはなっていない。