The original document is the following URL.
http://openjdk.java.net/groups/hotspot/docs/RuntimeOverview.html

License: GPLv2


HotSpot Runtime Overview
このセクションはHotSpotランタイムシステムの主要なサブシステムのキーコンセプトを紹介します。
次のトピックを紹介します。

コマンドライン引数の処理

Java HotSpot仮想マシンの性能に影響を与えるたくさんのコマンドライン引数オプションや環境変数があります。これらの中のいくつかのオプションはランチャによって使用されるもの (例えば `-server` や `-client`) や、いくつかはランチャによって処理されJVMに渡されますが、ほとんどのオプションは直接JVMに渡されます。

オプションは主に3つのカテゴリがあります。標準オプション(standard option)、非標準オプション(non-standard option)、開発オプション(developer option)です。
標準オプションは全てのJVM実装で利用可能かつリリース間でも安定しています。(depricatedになることはありますが)
-X で始まるオプションは非標準オプション(全てのJVM実装でサポートされることが保証されていない)で、今後のJava SDKのリリースで予告なく変更される可能性があります。
-XX で始まるオプションは開発オプションで正常に動作させるためには特定のシステム要件が必要であったり、システム設定パラメータへのアクセス権限が必要であったりする場合もあります。このオプションは通常利用には推奨されていません。このオプションも予告なく変更されることがあります。

コマンドラインフラグはJVM内部の変数を扱います。それらは型がありデフォルト値を持っています。booleanの値はコマンドラインが扱うその変数の値が単に存在するかしないかを表します。 -XX のbooleanフラグの '+' や '-' の名前の前につくプレフィックスはそれぞれ true か false の値です。追加で情報が必要になる値は、その情報を(引数として)渡すことによって利用できる異なるメカニズムがあることを表します。いくつかのフラグは名前の後にとくに記号もなく直接データが渡されることもあります。一方で他のフラグは名前の後とデータを ':' や '=' で分けないといけないものがあります。残念なことにこの方法は特定のフラグやパース方法に依存しています。開発フラグ(-XXフラグ) は次の3つの異なった形しかありません。
-XX:+OptionName」「-XX:-OptionNameそして-XX:OptionName=」です。

ほとんど全てのintegerサイズのオプションはサフィックスに 'k', 'm', 'g' が利用できます。それぞれキロ、メガ、ギガの単位で扱うために利用できます。これらの引数はよくメモリサイズを指定するときに使われます。

VMのライフサイクル
このセクションではHotSpot VMのライフサイクルに付属するJavaランチャの概要について説明します。

ランチャ
Java Standard EditionにはいくつかのHotSpot VMランチャがあります。UnixやWindowsのJavaコマンドで通常利用するランチャは java や javaw コマンドです。javaws はネットワークベースのランチャなので混同しないようにしてください。

VM起動時にランチャは以下のように動作します:
  1. コマンドライン引数を解析します。-client もしくは -server は適切なVMライブラリをロードするためにランチャ内で使用されます。その他の引数は JavaVMInitArgs 経由でVMに渡されます。
  2. ヒープサイズおよびコマンドラインで明示的に指定されていない場合はコンパイラタイプ(client/server)を確立します。
  3. LD_LIBRARY_PATH、CLASSPATH の環境変数を設定します。
  4. Main-Classクラスがコマンドラインで指定されていない場合、JARのマニフェストファイルにある Main-Class の名前を読み込みます。
  5. 新しく作成されたスレッド(最初にできたスレッドではない)内で JNI_CreateJavaVM 使用してVMを作成します。(備考: 最初にできたスレッドでVMを作成するとVMをカスタマイズすることによる性能劣化が大きくなります。例えばWindows上でのスタックサイズやその他の制約など)
  6. VMが作成,初期化され、Main-Classが読み込まれるとランチャはMain-Classからメインメソッドの属性を取得します。
  7. VM内で CallStaticVoidMethod とコマンドラインに渡された引数を使用してメインメソッドが呼び出されます。
  8. メインメソッド実行完了後、(とても重要なことに) 発生したペンディング中の例外をチェック、クリアし、exitステータスを返却します。例外は ExceptionOccurred を呼び出すことでクリアします。メソッドが成功の場合は戻り値が0になり、さもなければその他の値は呼び出し元のプロセスに返却されます。
  9. メインスレッドは DetachCurrentThread デタッチされます。デタッチするとスレッドカウントをデクリメントします。これにより DestroyJavaVMを安全に呼び出すことができ、かつスレッドが VM内の操作を実行していないことや JVM上のスタックにJavaフレームが存在しないことを保証します。

一番重要なフェーズは JNI_CreateJavaVM と DestroyJavaVM です。これらについて次のセクションで説明します。
JNI_CreateJavaVM
JNIの呼び出しメソッドは次のように実行します:
  1. 2つのスレッドが同時にこのメソッドを呼び出していないことや2つのVMインスタンスが同じプロセスで作成されていないことを保証します。初期化時に "point of no return" に到達すると同じプロセス空間にVMを作成することができなくなります。これはこのときVMが再度初期化できない静的データ構造を作成するためです。
  2. JNIのバージョンがサポートされているか確認し、GCロギングのためにostreamが初期化されます。乱数生成器、現在のpid、高精度計時、メモリーページサイズやガードページなどのOSモジュールが初期化されます。
  3. 後で使用するためにパースされ保存されていた引数やプロパティが渡されます。一般的なJavaのシステムプロパティが初期化されます。
  4. 同期化やスタックメモリ、セーフポイントページのためにパースされた引数やプロパティを基にしたOSモジュールが作成され初期化されます。このとき libzip、libhpi、libjava、libthreadが読み込まれ、シグナルハンドラが初期化され設定されます。そしてスレッドライブラリが初期化されます。
  5. 出力ストリームロガーが初期化されます。必要とされるすべてのエージェントライブラリ(hprofやjdi)が初期化され開始されます。
  6. スレッドの操作のために必要ないくつかのスレッドの特定のデータを保持するスレッド状態スレッド局所記憶(TLS)が初期化されます。
  7. イベントログ、OS synchronization primitives, perfMemory (performance memory), chunkPool (memory allocator)などの Iフェーズのグローバルデータが初期化されます。
  8. このとき Threads を作成できるようになります。メインスレッドのJavaバージョンが作成され現在のOSスレッドにアタッチされます。しかしこのスレッドはまだ Threads の known list には追加されていません。Javaレベルの同期化は初期化、有効化されます。
  9. BootClassLoaderCodeCacheInterpreterCompiler, JNI,SystemDictionary,Universeなどの残りのグローバルモジュールが初期化されます。"point of no return (もはや後に引けない段階)"に到達し、同じプロセスのアドレス空間に他のVMを作成することができなくなります。
  10. Thread_Lockを初めてロックしてメインスレッドがリストに追加されます。Universe(必要となるグローバルデータ構造のセット)の安全性チェックが行われ、VMの全ての重要な機能を実行する VMThread が作成されます。このとき、適切なJVMTIイベントが現在の状態を通知するために送信されます。
  11. java.lang.String,java.lang.System,java.lang.Thread,java.lang.ThreadGroup,java.lang.reflect.Method,java.lang.ref.Finalizer,java.lang.Classやその他のシステムクラスが読み込まれ初期化されます。このとき、VMは初期化され使用可能になります。しかしまだ完全には機能していません。
  12. シグナルハンドラスレッドが開始され、コンパイラが初期化されCompileBrokerスレッドが開始されます。 StatSampler and WatcherThreads のヘルパースレッドも開始される。このときVMの全ての機能が使用できるようになり、JNIEnv が設定され呼び出し元に返され、VMはJNIリクエストを受け付ける準備が整います。
DestroyJavaVM
このメソッドはVMを終了するためにランチャから呼び出されます。またとても深刻なエラーが発生したときにVM自身からも呼び出されます。
VMの終了は次のようなステップ行います:
  1. 最後のデーモンではないスレッドが実行するまで待ちます。VMはまだ機能しています。
  2. java.lang.Shutdown.shutdown() を呼び出します。このメソッドはJavaレベルのshutdown hookの呼び出しや finalization-on-exit のときにfinalizerを実行します。
  1. VMレベルのshutdown hooks(JVM_OnExit()に登録されている)に備えるためにbefore_exit()を実行ます。Profiler,StatSamplerWatcher and GCスレッドを停止します。ステータスイベントをJVMTI/PIに送信し、JVMPIを無効化、シグナルスレッドを停止します。
  1. JavaThread::exit()を呼び出してJNIハンドルブロックを解放、スタックガードページを削除、スレッドリストからこのスレッドを削除します。このときからあらゆるJavaコードは実行できなくなります。
  2. VMスレッドを停止するとVMをsafe pointに持ってきてコンパイラスレッドを停止します。safe pointでは"Safepoint"によりブロックされるので何も利用することができません。
  3. JNI/JVM/JVMPIバリアでトレースを無効化します。
  4. まだ実行しているネイティブコードのスレッドのために _vm_exited フラグを設定します。
  5. このスレッドを削除します。
  6. exit_globals()を呼び出して、IO や PerfMemoryリソースを削除します。
  7. 呼び出し元に返します。

VMのクラスローディング
Java HotSpot VMはJava言語仕様第3版[1]、Java仮想マシン仕様(JVMS)第2版[2] や更新されたJVMSの第5章の "Loading, Linking and Initializing"[3] の修正版で定義されたクラスローディングをサポートします。
VMはクラスやインターフェースのローディング、リンク、初期化が必要になるコンスタントプールのシンポルの解決を行います。"クラスローディング"という用語はクラスやインターフェース名とクラスオブジェクトのマッピングやJVMSのクラスローディングのフェーズで定義されたより具体的なローディング、リンク、初期化といった全体的なプロセスのことをいいます。
一般的なクラスローディングはクラスファイルの中のコンスタントプールのシンボルを解決するときのバイトコード解決のことをいいます。Class.forName(),classLoader.loadClass(), reflection APIs や JNI_FindClass といったJava APIはクラスローディングの初期化を行うことができます。VM自身もクラスローディングの初期化を行うことができます。VMは java.lang.Object,java.lang.Thread などのコアクラスをJVM起動時に読み込みます。クラスを読み込むときはそのクラスのすべての親クラス、親インターフェースを読み込む必要があります。またリンクフェーズのクラスファイルの検証ではさらに追加してクラスの読込みが必要になります。
VMやJava SEのクラスローディングライブラリはどちらもクラスローディングの責務を共有します。VMはコンスタントプール解決、リンクやクラスやインターフェースの初期化を行います。ローディングフェーズはVMと特定のクラスローダ(java.lang.classLoader)の協業で行います。

クラスローディングフェーズ
"ロードクラス"フェーズでは クラスやインターフェースの名前を取得しクラスファイルのフォーマットからバイナリを探し、クラスを定義して java.lang.Class オブジェクトを作成します。ロードクラスフェーズではバイナリ表現が見つからなかった場合 NoClassDefFound エラーを送出します。さらにロードクラスフェーズではクラスファイルシンタックスのフォーマットチェック時に ClassFormatError や UnsupportedClassVersionError を送出します。クラスのロードが完了する前に、VMはすべての親クラスと親インターフェースをロードします。もし再帰的に取得した親クラスや親インターフェースに問題があれば、VMは ClassCircularityError を送出します。また親インターフェースがインターフェースではなかった場合や、親クラスがインターフェースだった場合、VMはIncompatibleClassChangeError を送出します。

"リンククラス"フェーズは最初にクラスファイルのセマンティクスのチェック、コンスタントプールシンボルのチェックや型チェックといった検証を行います。これらのチェック時に VerifyError を送出します。リンキングは静的フィールドを作成しデフォルト値で初期化、メソッドテーブルの割当といった準備をします。ここではまだJavaコードは実行されません。リンキングは任意でシンボリック参照の解決を行います。

クラス初期化処理では静的初期化子と静的フィールドの初期化子を実行します。これがこのクラスで初めて実行されるJavaコードになります。クラス初期化処理は親インターフェースの初期化ではなく親クラスの初期化が必要になります。

JVMS(Java仮想マシン仕様)ではクラス初期化処理はクラスの初めての利用時に発生すると記載されています。JLS(Java言語仕様)は、その言語セマンティクスに従っている限りはリンキング時のシンボリック解決に関しては柔軟でローディング、(次のステップを実行する前の)リンキングや初期化のそれぞれのステップで完了して(プログラムが期待する)エラーを送出します。パフォーマンスの点ではHotSpot VMは一般的にクラス初期化処理をしクラスのロードとリンクが完了するまで待ちます。もしクラスAがクラスBを参照し、クラスAのローディングは(クラスの検証は必要になるのは除き)クラスBのローディングは必要になりません。Bを参照する初めての命令の実行はクラスBのローディングやリンキングが必要になるBの初期化処理を引き起こします。

クラスローダの委譲
クラスローダがクラスの検索とロードを要求されるとき、そのクラスローダは実際のローディングを他のクラスローダへ要求します。これはクラスローダの委譲と呼ばれます。最初のローダは初期化ローダ(initiating loader)で最終的に暮らすを定義するクラスローダは定義ローダ(defining loader)といいます。バイトコード解決では、初期化ローダはクラスのコンスタントプールシンボルを解決するクラスローダです。
クラスローダは階層構造で定義されておりそれぞれのクラスローダは委譲する親をもちます。委譲はバイナリのクラス表現の検索順を定義します。Java SEのクラスローダの階層構造は bootstrapクラスローダ、extensionクラスローダ、systemクラスローダの順に探します。systemクラスローダはデフォルトのアプリケーションクラスローダで、"main"を実行しクラスパスからクラスをロードします。アプリケーションクラスローダはJava SEクラスローダライブラリのクラスローダにもなりますし、アプリケーション開発者から提供されたりもします。Java SEクラスローダライブラリはJREのlib/extディレクトリからクラスをロードするextensionクラスローダを実装します。

Bootstrapクラスローダ
VMは例えばrt.jarを含む BOOTPATH からクラスをロードするbootstrapクラスローダを実装します。さらに速く起動するためにVMはクラスデータの共有(Class Data Sharing)を利用して事前にロードしされたクラスも処理します。

型安全
クラスやインターフェースの名前はパッケージ名を含んだ完全に修飾された名前で定義されます。クラスの型はその完全に修飾された一意となる名前とクラスローダから決定します。なのでクラスローダは名前空間を定義し、2つの異なるクラスローダからロードされた同じクラス名は異なるクラスの型になります。
カスタムクラスローダの存在を考えると、VMは行儀の悪いクラスローダが型の安全性に違反しないことを保証する責任があります。"Dynamic Class Loading in the Java Virtual Machine"[4] や JVMS 5.3.4[2] を参照してください。VMは、クラスA が B.foo() を呼び出したときに、AのクラスローダとBのクラスローダがfooのパラメータについて合意し、追跡し、ローダ制約をチェックすることで、値を返すことが保証されます。

HotSpotのクラスのメタデータ
クラスローディングは instanceKlass や arrayKlass をGCのパーマネント領域に生成します。instanceKlass はこのクラスをミラーリングした java.lang.Class のインスタンスであJavaミラーを参照します。VMのC++は klassOop 経由で instanceKlass にアクセスします。

HotSpot Internal Class Loading Data
HotSpot VMは、クラスのロードを追跡するために、3つの主要なハッシュテーブルを扱います。SystemDictionary には klassOop に紐づいたクラス名/クラスローダのペアのロードされたクラスが含まれています。SystemDictionary には"クラス名/初期化ローダ"および"クラス名/定義ローダ"のペアが含まれています。エントリは現在はsafepointでのみ削除されます。PlaceholderTable は現在までにロードされたクラスを含みます。これは ClassCircularityErrorchecking やマルチスレッドクラスドーディングをサポートしたクラスローダの並列クラスローディングに使用されます。LoaderConstraintTable は型安全チェックの制約を追跡します。
これらのハッシュテーブルは、すべて SystemDictionary_lock により保護されています。一般的に、VM内のロードクラスフェーズでは、クラスローダオブジェクトのロック(the Class loader object lock)を使用して直列化されています。

Bytecode Verifier and Format Checker

The Java language is a type-safe language, and standard Java compilers produce valid classfiles and type-safe code, but the JVM can't guarantee that the code was produced by a trustworthy compiler, so it must reestablish that type-safety through a process at link-time called bytecode verification.

Bytecode verification is specified in section 4.8 of the Java Virtual Machine Specification. The specification prescribes both static and dynamic constraints on the code which the JVM verifies. If any violations are found, the VM will throw a VerifyError and prevent the class from being linked.

Many of the constraints on the bytecodes can be checked statically, such as the operand of an ‘ldccode must be a valid constant pool index whose type isCONSTANT_Integer,CONSTANT_StringorCONSTANT_Float. Other constraints which check the type and number of arguments for other instructions requires dynamic analysis of the code to determine which operands will be present on the expression stack during execution.

There are currently two methods of analyzing the bytecodes to determine the types and number of operands that will be present for each instruction. The traditional method is called “type inference”, and operates by performing an abstract interpretation of each bytecode and merging type states at branch targets or exception handles. The analysis iterates over the bytecode until a steady state for the types are found. If a steady state cannot be found, or if the resulting types violate some bytecode constraint, then a VerifyError is thrown. The code for this verification step is present in the libverify.so external library, and uses JNI to gather whatever information is needed about classes and types.

New in JDK6 is the second method for verification which is called “type verification”. In this method the Java compiler provides the steady-state type information for each branch or exception target, via the code attribute, StackMapTable. The StackMapTable consists of a number of stack map frames, each which indicates the types of the items on the expression stack and in the local variables at some offset in the method. The JVM needs to then only perform one pass through the bytecode to verify the correctness of the types to verify the bytecode. This is the method already used by JavaME CLDC. Since it it smaller and faster, this method of verification is built directly in the VM itself.

For all classfiles with a version number less than 50, such as those created prior to JDK6, the JVM will use the traditional type inference method to verify the classfiles. For classfiles greater than or equal to 50, the StackMapTable attributes will be present and the new verifier will be used. Because of the possibility of older external tools that might instrument the bytecode but neglect to update the StackMapTable attribute, certain verification errors that occur during type-checking verification may failover to the type-inference method. Should that pass succeed, the class file will be verified.

Class Data Sharing

Class data sharing (CDS) is a feature introduced in J2SE 5.0 that is intended to reduce the startup time for Java programming language applications, in particular smaller applications, as well as reduce footprint. When the JRE is installed on 32-bit platforms using the Sun provided installer, the installer loads a set of classes from the system jar file into a private internal representation, and dumps that representation to a file, called a “shared archive”. If the Sun JRE installer is not being used, this can be done manually, as explained below. During subsequent JVM invocations, the shared archive is memory-mapped in, saving the cost of loading those classes and allowing much of the JVM's metadata for these classes to be shared among multiple JVM processes.

Class data sharing is supported only with the Java HotSpot Client VM, and only with the serial garbage collector.

The primary motivation for including CDS is the decrease in startup time it provides. CDS produces better results for smaller applications because it eliminates a fixed cost: that of loading certain core classes. The smaller the application relative to the number of core classes it uses, the larger the saved fraction of startup time.

The footprint cost of new JVM instances has been reduced in two ways. First, a portion of the shared archive, currently between five and six megabytes, is mapped read-only and therefore shared among multiple JVM processes. Previously this data was replicated in each JVM instance. Second, since the shared archive contains class data in the form in which the Java Hotspot VM uses it, the memory which would otherwise be required to access the original class information inrt.jaris not needed. These savings allow more applications to be run concurrently on the same machine. On Microsoft Windows, the footprint of a process, as measured by various tools, may appear to increase, because a larger number of pages are being mapped in to the process' address space. This is offset by the reduction in the amount of memory (inside Microsoft Windows) which is needed to hold portions onrt.jar. Reducing footprint remains a high priority.

In HotSpot, the class data sharing implementation introduces new Spaces into the permanent generation which contain the shared data. The classes.jsa shared archive is memory mapped into these Spaces at VM startup. Subsequently, the shared region is managed by the existing VM memory management subsystem.

Read-only shared data includes constant method objects (constMethodOops), symbol objects (symbolOops), and arrays of primitives, mostly character arrays.

Read-write shared data consists of mutable method objects (methodOops), constant pool objects (constantPoolOops), VM internal representation of Java classes and arrays (instanceKlasses andarrayKlasses), and variousString,Class, andExceptionobjects.

Interpreter

The current HotSpot interpreter, which is used for executing bytecodes, is a template based interpreter. The HotSpot runtime a.k.a.InterpreterGeneratorgenerates an interpreter in memory at the startup using the information in theTemplateTable(assembly code corresponding to each bytecode). A template is a description of each bytecode. TheTemplateTabledefines all the templates and provides accessor functions to get the template for a given bytecode. The non-product flag-XX:+PrintInterpretercan be used to view the template table generated in memory during the VM's startup process.

The template design performs better than a classic switch-statement loop for several reasons. First, the switch statement performs repeated compare operations, and in the worst case it may be required to compare a given command with all but one bytecodes to locate the required one. Second, it uses a separate software stack to pass Java arguments, while the native C stack is used by the VM itself. A number of JVM internal variables, such as the program counter or the stack pointer for a Java thread, are stored in C variables, which are not guaranteed to be always kept in the hardware registers. Management of these software interpreter structures consumes a considerable share of total execution time.[5]

Overall, the gap between the VM and the real machine is significantly narrowed by the HotSpot interpreter, which makes the interpretation speed considerably higher. This, however, comes at a price of e.g. large machine-specific chunks of code (roughly about 10 KLOC (thousand lines of code) of Intel-specific and 14 KLOC of SPARC-specific code). Overall code size and complexity is also significantly higher, since e.g. the code supporting dynamic code generation is needed. Obviously, debugging dynamically generated machine code is significantly more difficult than static code. These properties certainly do not facilitate implementation of runtime evolution, but they don’t make it infeasible either.[5]

The interpreter calls out to the VM runtime for complex operations (basically anything too complicated to do in assembly language) such as constant pool lookup.

The HotSpot interpreter is also a critical part of the overall HotSpot adaptive optimization story. Adaptive optimization solves the problems of JIT compilation by taking advantage of an interesting program property. Virtually all programs spend the vast majority of their time executing a minority of their code. Rather than compiling method by method, just in time, the Java HotSpot VM immediately runs the program using an interpreter, and analyzes the code as it runs to detect the critical hot spots in the program. Then it focuses the attention of a global native-code optimizer on the hot spots. By avoiding compilation of infrequently executed code (most of the program), the Java HotSpot compiler can devote more attention to the performance-critical parts of the program, without necessarily increasing the overall compilation time. This hot spot monitoring is continued dynamically as the program runs, so that it literally adapts its performance on the fly to the user's needs.

Java Exception Handling

Java virtual machines use exceptions to signal that a program has violated the semantic constraints of the Java language. For example, an attempt to index outside the bounds of an array will cause an exception. An exception causes a non-local transfer of control from the point where the exception occurred (or wasthrown) to a point specified by the programmer (or where the exception iscaught).[6]

The HotSpot interpreter, dynamic compilers, and runtime all cooperate to implement exception handling. There are two general cases of exception handling: either the exception is thrown or caught in the same method, or it's caught by a caller. The latter case is more complicated and requiresstack unwindingto find the appropriate handler.

Exceptions can be initiated by thethrowbytecode, a return from a VM-internal call, a return from a JNI call, or a return from a Java call. (The last case is really just a later stage of the first 3.) When the VM recognizes that an exception has been thrown, the runtime system is invoked to find the nearest handler for that exception. Three pieces of information are used to find the handler; the current method, the current bytecode, and the exception object. If a handler is not found in the current method, as mentioned above, the current activation stack frame is popped and the process is iteratively repeated for previous frames.

Once the correct handler is found, the VM execution state is updated, and we jump to the handler as Java code execution is resumed.

Synchronization

Broadly, we can define “synchronization” as a mechanism that prevents, avoids or recovers from the inopportune interleavings (commonly called “races”) of concurrent operations. In Java, concurrency is expressed through the thread construct. Mutual exclusion is a special case of synchronization where at most a single thread is permitted access to protected code or data.

HotSpot provides Java monitors by which threads running application code may participate in a mutual exclusion protocol. A monitor is either locked or unlocked, and only one thread may own the monitor at any one time. Only after acquiring ownership of a monitor may a thread enter the critical section protected by the monitor. In Java, critical sections are referred to as "synchronized blocks", and are delineated in code by thesynchronizedstatement.

If a thread attempts to lock a monitor and the monitor is in an unlocked state, the thread will immediately gain ownership of the monitor. If a subsequent thread attempts to gain ownership of the monitor while the monitor is locked that thread will not be permitted to proceed into the critical section until the owner releases the lock and the 2nd thread manages to gain (or is granted) exclusive ownership of the lock.

Some additional terminology: to “enter” a monitor means to acquire exclusive ownership of the monitor and enter the associated critical section. Likewise, to “exit” a monitor means to release ownership of the monitor and exit the critical section. We also say that a thread that has locked a monitor now “owns” that monitor. “Uncontended” refers to synchronization operations on an otherwise unowned monitor by only a single thread.

The HotSpot VM incorporates leading-edge techniques for both uncontended and contended synchronization operations which boost synchronization performance by a large factor.

Uncontended synchronization operations, which comprise the majority of synchronizations, are implemented with constant-time techniques. Withbiased locking, in the best case these operations are essentially free of cost. Since most objects are locked by at most one thread during their lifetime, we allow that thread tobiasan object toward itself. Once biased, that thread can subsequently lock and unlock the object without resorting to expensive atomic instructions.[7]

Contended synchronization operations use advanced adaptive spinning techniques to improve throughput even for applications with significant amounts of lock contention. As a result, synchronization performance becomes so fast that it is not a significant performance issue for the vast majority of real-world programs.

In HotSpot, most synchronization is handled through what we call ””fast-path” code. We have two just-in-time compilers (JITs) and an interpreter, all of which will emit fast-path code. The two JITs are “C1”, which is the-clientcompiler, and “C2”, which is the-servercompiler. C1 and C2 both emit fast-path code directly at the synchronization site. In the normal case when there's no contention, the synchronization operation will be completed entirely in the fast-path. If, however, we need to block or wake a thread (in monitorenter or monitorexit, respectively), the fast-path code will call into the slow-path. The slow-path implementation is in native C++ code while the fast-path is emitted by the JITs.

Per-object synchronization state is encoded in the first word (the so-calledmark word) of the VM's object representation. For several states, the mark word is multiplexed to point to additional synchronization metadata. (As an aside, in addition, the mark word is also multiplexed to contain GC age data, and the object's identity hashCode value.) The states are:

Thread Management

Thread management covers all aspects of the thread lifecycle, from creation through to termination, and the coordination of threads within the VM. This involves management of threads created from Java code (whether application code or library code), native threads that attach directly to the VM, or internal VM threads created for a range of purposes. While the broader aspects of thread management are platform independent, the details necessarily vary depending on the underlying operating system.

Threading Model

The basic threading model in Hotspot is a 1:1 mapping between Java threads (an instance ofjava.lang.Thread) and native operating system threads. The native thread is created when the Java thread is started, and is reclaimed once it terminates. The operating system is responsible for scheduling all threads and dispatching to any available CPU.

The relationship between Java thread priorities and operating system thread priorities is a complex one that varies across systems. These details are covered later.

Thread Creation and Destruction

There are two basic ways for a thread to be introduced into the VM: execution of Java code that callsstart()on ajava.lang.Threadobject; or attaching an existing native thread to the VM using JNI. Other threads created by the VM for internal purposes are discussed below.

There are a number of objects associated with a given thread in the VM (remembering that Hotspot is written in the C++ object-oriented programming language):

When ajava.lang.Threadis started the VM creates the associatedJavaThreadandOSThreadobjects, and ultimately the native thread. After preparing all of the VM state (such as thread-local storage and allocation buffers, synchronization objects and so forth) the native thread is started. The native thread completes initialization and then executes a start-up method that leads to the execution of thejava.lang.Threadobject'srun()method, and then, upon its return, terminates the thread after dealing with any uncaught exceptions, and interacting with the VM to check if termination of this thread requires termination of the whole VM. Thread termination releases all allocated resources, removes theJavaThreadfrom the set of known threads, invokes destructors for theOSThreadandJavaThreadand ultimately ceases execution when it's initial startup method completes.

A native thread attaches to the VM using the JNI callAttachCurrentThread. In response to this an associatedOSThreadandJavaThreadinstance is created and basic initialization is performed. Next ajava.lang.Threadobject must be created for the attached thread, which is done by reflectively invoking the Java code for theThreadclass constructor, based on the arguments supplied when the thread attached. Once attached, a thread can invoke whatever Java code it needs to via the other JNI methods available. Finally when the native thread no longer wishes to be involved with the VM it can call the JNIDetachCurrentThreadmethod to disassociate it from the VM (release resources, drop the reference to thejava.lang.Threadinstance, destruct theJavaThreadandOSThreadobjects and so forth).

A special case of attaching a native thread is the initial creation of the VM via the JNICreateJavaVMcall, which can be done by a native application or by the launcher (java.c). This causes a range of initialization operations to take place and then acts effectively as if a call toAttachCurrentThreadwas made. The thread can then invoke Java code as needed, such as reflective invocation of themainmethod of an application. See the JNI section for further details.

Thread States

The VM uses a number of different internal thread states to characterize what each thread is doing. This is necessary both for coordinating the interactions of threads, and for providing useful debugging information if things go wrong. A thread's state transitions as different actions are performed, and these transition points are used to check that it is appropriate for a thread to proceed with the requested action at that point in time – see the discussion of safepoints below.

The main thread states from the VM perspective are as follows:

For debugging purposes additional state information is also maintained for reporting by tools, in thread dumps, stack traces etc. This is maintained in theOSThreadand some of it has fallen into dis-use, but states reported in thread dumps etc include:

Other subsystems and libraries impose their own state information, such as the JVMTI system and theThreadStateexposed by thejava.lang.Threadclass itself. Such information is generally not accessible to, nor relevant to, the management of threads inside the VM.

Internal VM Threads

People are often surprised to discover that even executing a simple “Hello World” program can result in the creation of a dozen or more threads in the system. These arise from a combination of internal VM threads, and library related threads (such as reference handler and finalizer threads). The main kinds of VM threads are as follows:

All threads are instances of theThreadclass, and all threads that execute Java code areJavaThreadinstances (a subclass ofThread). The VM keeps track of all threads in a linked-list known as theThreads_list, and which is protected by theThreads_lock– one of the key synchronization locks used within the VM.

VM Operations and Safepoints

TheVMThreadspends its time waiting for operations to appear in theVMOperationQueue, and then executing those operations. Typically these operations are passed on to theVMThreadbecause they require that the VM reach asafepoint before they can be executed. In simple terms, when the VM is at safepoint all threads inside the VM have been blocked, and any threads executing in native code are prevented from returning to the VM while the safepoint is in progress. This means that the VM operation can be executed knowing that no thread can be in the middle of modifying the Java heap, and all threads are in a state such that their Java stacks are unchanging and can be examined.

The most familiar VM operation is for garbage collection, or more specifically for the “stop-the-world” phase of garbage collection that is common to many garbage collection algorithms. But many other safepoint based VM operations exist, for example: biased locking revocation, thread stack dumps, thread suspension or stopping (i.e. Thejava.lang.Thread.stop()method) and numerous inspection/modification operations requested through JVMTI.

Many VM operations are synchronous, that is the requestor blocks until the operation has completed, but some are asynchronous or concurrent, meaning that the requestor can proceed in parallel with theVMThread(assuming no safepoint is initiated of course).

Safepoints are initiated using a cooperative, polling-based mechanism. In simple terms, every so often a thread asks “should I block for a safepoint?”. Asking this question efficiently is not so simple. One place where the question is often asked is during a thread state transition. Not all state transitions do this, for example a thread leaving the VM to go to native code, but many do. The other places where a thread asks are in compiled code when returning from a method or at certain stages during loop iteration. Threads executing interpreted code don't usually ask the question, instead when the safepoint is requested the interpreter switches to a different dispatch table that includes the code to ask the question; when the safepoint is over, the dispatch table is switched back again. Once a safepoint has been requested, theVMThreadmust wait until all threads are known to be in a safepoint-safe state before proceeding to execute the VM operation. During a safepoint theThreads_lockis used to block any threads that were running, with theVMThreadfinally releasing theThreads_lockafter the VM operation has been performed.

C++ Heap Management

In addition to the Java heap, which is maintained by the Java heap manager and garbage collectors, HotSpot also uses the C/C++ heap (also called the malloc heap) for storage of VM-internal objects and data. A set of C++ classes derived from the base classArenais used to manage C++ heap operations.

Arenaand its subclasses provide a fast allocation layer that sits on top of malloc/free. EachArenaallocates memory blocks (orChunks)out of 3 globalChunkPools.EachChunkPoolsatisfies allocation requests for a distinct range of allocation sizes. For example, a request for 1k of memory will be allocated from the “small”ChunkPool, while a 10K allocation will be made from the "medium"ChunkPool. This is done to avoid wasteful memory fragmentation.

TheArenasystem also provides better performance than pure malloc/free. The latter operations may require acquisition of global OS locks, which affects scalability and can hurt performance.Arenas are thread-local objects which cache a certain amount of storage, so that in the fast-path allocation case a lock is not required. Likewise,Arenafree operations do not require a lock in the common case.

Arenas are used for thread-local resource management (ResourceArea) and handle management (HandleArea). They are also used by both the client and server compilers during compilation.

Java Native Interface (JNI)

The JNI is a native programming interface. It allows Java code that runs inside a Java virtual machine to interoperate with applications and libraries written in other programming languages, such as C, C++, and assembly.

While applications can be written entirely in Java, there are situations where Java alone does not meet the needs of an application. Programmers use the JNI to writeJava native methodsto handle those situations when an application cannot be written entirely in Java.

JNI native methods can be used to create, inspect, and update Java objects, call Java methods, catch and throw exceptions, load classes and obtain class information, and perform runtime type checking.

The JNI may also be used with theInvocation APIto enable an arbitrary native application to embed the Java VM. This allows programmers to easily make their existing applications Java-enabled without having to link with the VM source code. [9]

It is important to remember that once an application uses the JNI, it risks losing two benefits of the Java platform.

First, Java applications that depend on the JNI can no longer readily run on multiple host environments. Even though the part of an application written in the Java programming language is portable to multiple host environments, it will be necessary to recompile the part of the application written in native programming languages.

Second, while the Java programming language is type-safe and secure, native languages such as C or C++ are not. As a result, Java developers must use extra care when writing applications using the JNI. A misbehaving native method can corrupt the entire application. For this reason, Java applications are subject to security checks before invoking JNI features.

As a general rule, developers should architect the application so that native methods are defined in as few classes as possible. This entails a cleaner isolation between native code and the rest of the application.[10]

In HotSpot, the implementation of the JNI functions is relatively straightforward. It uses various VM internal primitives to perform activities such as object creation, method invocation, etc. In general, these are the same runtime primitives used by other subsystems such as the interpreter.

A command line option,-Xcheck:jni, is provided to aid in debugging problems in JNI usage by native methods. Specifying-Xcheck:jnicauses an alternate set of debugging interfaces to be used by JNI calls. The alternate interface verifies arguments to JNI calls more stringently, as well as performing additional internal consistency checks.

HotSpot must take special care to keep track of which threads are currently executing in native methods. During some VM activities, notably some phases of garbage collection, one or more threads must be halted at asafepointin order to guarantee that the Java memory heap is not modified during the sensitive activity. When we wish to bring a thread executing in native code to a safepoint, it is allowed to continue executing native code, but the thread will be stopped when it attempts to return into Java code or make a JNI call.

VM Fatal Error Handling

It is very important to provide easy ways to handle fatal errors for any software. Java Virtual Machine, i.e. JVM is not an exception. A typical fatal error would beOutOfMemoryError. Another common fatal error on Windows is calledAccess Violationerror which is equivalent toSegmentation Faulton Solaris/Linux platforms. It is critical to understand the cause of these kind of fatal errors in order to fix them either in your application or sometimes, in JVM itself.

Usually when JVM crashes on a fatal error, it will dump a hotspot error log file calledhs_err_pid<pid>.log, (where<pid>is replaced by the crashed java process id) to the Windows desktop or the current application directory on Solaris/Linux. Several enhancements have been made to improve the diagnosability of this file since JDK 6 and many of them have been back ported to the JDK-1.4.2_09 release. Here are some highlights of these improvements:

Another important feature is you can specify-XX:OnError="cmd1 args...;com2 ..."to the java command so that whenever VM crashes, it will execute a list of commands you specified within the quotes shown above. A typical usage of this feature is you can invoke the debugger such as dbx or Windbg to look into the crash when that happens. For the earlier releases, you can specify
-XX:+ShowMessageBoxOnErroras a runtime option so that when VM crashes, you can attach the running Java process to your favorite debugger.

Having said something about HotSpot error log files, here is a brief summary on how JVM internally handles fatal errors.

SinceOutOfMemoryErroris so common to some large scale applications, it is critical to provide useful diagnostic message to users so that they could quickly identify a solution, sometimes by just specifying a larger Java heap size. WhenOutOfMemoryErrorhappens, the error message will indicate which type of memory is problematic. For example, it could be Java heap space or PermGen space etc. Since JDK 6, a stack trace will be included in the error message. Also,
-XX:OnOutOfMemoryError="<cmd>"option was invented so that a command will be run when the first OutOfMemoryError is thrown. Another nice feature that is worth mentioning is a built-in heap dump at OutOfMemoryError. It is enabled by specifying-XX:+HeapDumpOnOutOfMemoryErroroption and you can also tell the VM where to put the heap dump file by specifying
-XX:HeapDumpPath=<pathname>.

Even though applications are carefully written to avoid deadlocks, sometimes it still happens. When deadlock occurs, you can type “Ctrl+Break” on Windows or grab the Java process id and send SIGQUIT to the hang process on Solaris/Linux. A Java level stack trace will be dumped out to the standard out so that you can analyze the reasons of deadlock. Since JDK 6, this feature has been built into jconsole which is a very useful tool in the JDK. So when the application hangs on a deadlock, use jconsole to attach the process and it will analyze which lock is problematic. Most of the time, the deadlock is caused by acquiring locks in the wrong order.

We strongly encourage you to check out the “Trouble-Shooting and Diagnostic Guide”[11]. It contains a lot of information which might be very useful to diagnose fatal errors.

Further Reading

“Resolving the Mysteries of Java SE Classloader”, Jeff Nisewanger, Karen Kinnear, JavaOne 2006.

References

[1] Java Language Specification, Third Edition. Gosling, Joy, Steele, Bracha.http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.2

[2] Java Virtual Machine Specification, Second Edition. Tim Lindholm, Frank Yellin.http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html

[3] Amendment to Java Virtual Machine Specification. Chapter 5: Loading, Linking and Initializing.http://java.sun.com/docs/books/vmspec/2nd-edition/ConstantPool.pdf

[4] Dynamic Class Loading in the Java Virtual Machine. Shen Liang, Gilad Bracha. Proc. of the ACM Conf. on Object-Oriented Programming, Systems, Languages and Applications, October 1998http://www.bracha.org/classloaders.ps

[5] “Safe Clsss and Data Evolution in Large and Long-Lived Java Applications”, Mikhail Dmitriev,http://research.sun.com/techrep/2001/smli_tr-2001-98.pdf

[6] Java Language Specification, Third Edition. Gosling, Joy, Steele, Bracha.http://java.sun.com/docs/books/jls/third_edition/html/exceptions.html

[7] “Biased Locking in HotSpot”.http://blogs.oracle.com/dave/entry/biased_locking_in_hotspot

[8] “Let’s say you’re interested in using HotSpot as a vehicle for synchronization research ...”.http://blogs.oracle.com/dave/entry/lets_say_you_re_interested

[9] “Java Native Interface Specifications”http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html

[10] “The Java Native Interface Programmer’s Guide and Specification”, Sheng Liang,http://java.sun.com/docs/books/jni/html/titlepage.html

[11] “Trouble-Shooting and Diagnostic Guide”http://java.sun.com/javase/6/webnotes/trouble/