はじめに
今後もアプリケーションをJavaで開発、運用していくことを前提にすると、そうした業務に携わる方は次のようなことを学び続けるでしょう。
- Javaの半年ごとのバージョンアップに追随して新機能などを学ぶ
- アーキテクチャなどでの新しい考え方をJavaで実現する方法を学ぶ
- 新しく出てきたJavaのフレームワークやライブラリの使い方を学ぶ
これらに加えてもう1つ知ってほしいことがあります。本連載で扱う内容であるJava仮想マシン(Java Virtual Machine:以下、JVM)です。JVMはJavaアプリケーション、Javaプログラムの実行を担っています。
javaコマンドを実行する、あるいはTomcatなどのサーブレットコンテナやアプリケーションサーバを起動してアプリケーションをデプロイすると、皆さんのアプリケーションが実行されますがその裏側ではJVMが動いています。
JVMについてあまり知らなくてもアプリケーションは動かせますが、それはアプリケーションが正常に動作している間だけとも言えます。障害発生など異常事態のとき、JVMに関する知識があれば解決までの時間が短縮できるでしょう。そのため、次のことも学ぶとよりよいと筆者は考えています。
- Javaプログラムの実行を担うJVMの仕組みやその情報を収集する方法について学ぶ
筆者は株式会社NTTデータでJavaサポートチームに所属し、Javaアプリケーションの障害対応などトラブルシューティングを業務の1つとしています。なお、この業務に携わるきっかけはJVMに詳しくなりたいという思いからでした。Javaのオープンソース実装であるOpenJDKにコントリビュートもしており、Authorという開発者ロールを保持しています。
本連載ではそうした経験やJavaコミュニティでの活動で得た知識を整理し、JVMの周辺ツールによる情報収集を通じてJVMを解説します。普段Javaを使っているけれどJVMについて知らない方にも読んでいただけたらと思います。本連載を通じて皆さんがJVMライフをより楽しんでいただければ幸いです。
対象読者
対象読者として、Javaプログラミングの「中級者」を想定しています。中級の明確な基準は作れませんが、企業などで実用のJavaアプリケーション開発や運用の経験があると十分です。Java仮想マシン(JVM)関連のツールの使用方法を理解したい方や、将来プログラミング言語Java自体の開発に携わってみたい方にはさらに興味深い内容になります。
JVM仕様とその実装
本連載はJVMの内部を文章で解説するものではなく、その情報を取得するさまざまなツールの利用を通じて、最終的にJVMについての知識を深めるというものです。とはいえJVMについてあらかじめ概要を知っておく方が理解を深められるため、連載第1回の前半ではJVMの概要を解説します。
またJVMと一言で言っても、実装は1つではありません。言語としてのJavaにはJava言語仕様があるように、JVMもJava仮想マシン仕様で仕様が定められています。
Java仮想マシン仕様を満たせば(厳密には加えて互換性テストキット「TCK」を通過させれば)JVMであり、実際にJVMは複数の実装があります。本連載ではJVMとしてOpenJDKのHotSpot VMを対象に解説します。
複数あるJDKとJVM実装
皆さんはJDKのバイナリがさまざまなところから提供されていることをご存じでしょうか? JDKはJava Development Kitのことで、Javaでの開発に必要なもの一式と捉えればよいでしょう。
アプリケーション開発運用の文脈では、JavaのインストールとはJDKのインストールを指します。Javaはオラクル社が中心となり開発している言語ですが、そのオラクル社が出しているJDK「Oracle JDK」以外にもさまざまなJDKがあります。
例を挙げると、Oracle OpenJDK(Oracle JDKと別個のものです)、Amazon Corretto、Microsoft Build of OpenJDK、Eclipse AdoptiumのEclipse Temurinなど数多くあります。それぞれ仕様を満たしている正式なJavaです。
ただし、こうしたJDKの多くがJavaのオープンソース実装であるOpenJDKのソースをベースにしています。OpenJDK自体はバイナリを提供しないため、企業や財団などがOpenJDKのソースをビルドしてJDKを提供しています。名称にOpenJDKという文字が入っていなくてもOpenJDKベースのものもあります。
OpenJDKベースのJDKはOpenJDKディストリビューション、もしくは単にJDKディストリビューションとまとめられています。ディストリビューションという言葉の意味はLinuxにおけるディストリビューションをイメージしてください。
OpenJDKにおけるJVMの実装は、HotSpotあるいはHotSpot VMと呼ばれます。言い換えるとOpenJDKベースのJDKであればJVMはHotSpot VMを使います。JVMの実装という観点では、HotSpot VM以外のJVMとしてIBM社がオープンソースで公開し、Eclipse Foundationに寄贈したEclipse OpenJ9(※1)やAzul社の商用JVMであるAzul Platform Prime(旧Zing)などがあります。
(※1):詳細な話ですが、OpenJDKをベースにしつつJVMをHotSpot VMからEclipse OpenJ9に変更したものとして、IBM社からIBM Semeru Runtime Open EditionというJDKが提供されています。以前はAdoptOpenJDKもOpenJDKでOpenJ9を使用するJDKのバイナリを提供していましたが、AdoptOpenJDKがEclipse Foundationに移管され、Eclipse Adoptiumとなって以降OpenJ9のものを提供しなくなりました。
JVMの概要
Javaプログラムを書いた後そのプログラムが実行されるまでの過程を整理しましょう。まず、プログラムをJavaコンパイラでコンパイルします。javacコマンドを実行してコンパイルし、Javaのソースファイルからクラスファイルを生成します。javaコマンドを実行するとJVMはクラスファイルを読み、バイトコードで表現された命令を実行してJavaプログラムにある処理を実行します。
以下にJVMの主要なコンポーネントを図で示します。詳細を述べれば、以下の図にあるもの以外のコンポーネントもありますが、JVMの概要を把握する分にはこの図の内容でよいでしょう。
まずクラスローダーがクラスファイルをロードします。クラスファイルに含まれるバイトコード命令をインタプリタが逐次解釈して処理を実行します。逐次解釈であるため、実行速度が遅くなるという欠点があります。そこでJIT(Just-in-Time)コンパイラが実行頻度の高い処理を機械語にコンパイルし、以降その機械語を実行するようにして高速化します。
JVM関連の文書や講演では、コンパイルという言葉をjavacではなくJITコンパイルに対して用いることも多いため、留意してください。なお「実行頻度が高い場所」を英語にすると「hot spot」です。これがHotSpot VMの名前に通じています。
ヒープはJavaヒープとも呼ばれますが、これはJVMがJavaのオブジェクトに割り当てるためのメモリ領域です。ヒープという言葉は文脈によって意味や指す範囲が異なることがありますが、本連載では特に記載がない限りこの領域を指します。
アプリケーションは処理が進むにつれて、以後決して使用することがないオブジェクトが出てきます。こうしたオブジェクトに対して割り当てたメモリ領域をそのままにしておけば、ヒープのメモリ領域が枯渇し、アプリケーションの動作に影響が出ます。
そうならないように、JVMはもはや使うことがないオブジェクトに割り当てているメモリ領域を再度利用できるようにします。これをオブジェクトの回収と言いますが、要はメモリ領域の解放です。回収をするコンポーネントがガベージコレクタであり、回収する処理をガベージコレクションと呼びます。
ガベージはGarbageでゴミ、コレクションはCollectionで収集、つまりゴミ収集です。この英単語の頭文字を取って短くGCと表記することも多いです。HotSpot VMでは回収方式であるGCアルゴリズムもデフォルトのG1GC(※2)以外にパラレルGCやZGCなど複数から選択できます。GCやGCアルゴリズムについては記事や書籍で多く取り上げられているため本連載では扱わない予定です。
(※2):Java 9以降はG1GCがデフォルトのGCアルゴリズムです。Java 8は実行環境によりパラレルGCもしくはシリアルGCが選択されます。