1. 序章
このチュートリアルでは、JVMのコードキャッシュメモリを簡単に見て学習します。
2. コードキャッシュとは何ですか?
簡単に言うと、 JVMコードキャッシュは、JVMがネイティブコードにコンパイルされたバイトコードを格納する領域です。 実行可能ネイティブコードの各ブロックをnmethodと呼びます。 nmethod は、完全またはインラインのJavaメソッドである可能性があります。
ジャストインタイム(JIT)コンパイラは、コードキャッシュ領域の最大の消費者です。 そのため、一部の開発者はこのメモリをJITコードキャッシュと呼んでいます。
3. コードキャッシュの調整
コードキャッシュのサイズは固定されています。 いっぱいになると、JITコンパイラがオフになっているため、JVMは追加のコードをコンパイルしません。 さらに、「CodeCacheがいっぱいです…コンパイラが無効になりました」という警告メッセージが表示されます。 その結果、アプリケーションのパフォーマンスが低下することになります。 これを回避するために、次のサイズオプションを使用してコードキャッシュを調整できます。
- InitialCodeCacheSize –初期コードキャッシュサイズ、デフォルトは160K
- ReservedCodeCacheSize –デフォルトの最大サイズは48MBです
- CodeCacheExpansionSize –コードキャッシュの拡張サイズ、32KBまたは64KB
ReservedCodeCacheSize を増やすことは解決策になる可能性がありますが、これは通常、一時的な回避策にすぎません。
幸い、JVMは UseCodeCacheFlushingオプションを提供して、コードキャッシュ領域のフラッシュを制御します。 デフォルト値はfalseです。 これを有効にすると、次の条件が満たされたときに占有領域が解放されます。
- コードキャッシュがいっぱいです。 この領域は、そのサイズが特定のしきい値を超えるとフラッシュされます
- 最後のクリーンアップから一定の間隔が経過している
- プリコンパイルされたコードは十分に熱くありません。 コンパイルされたメソッドごとに、JVMは特別なホットネスカウンターを追跡します。 このカウンターの値が計算されたしきい値よりも小さい場合、JVMはこのプリコンパイルされたコードを解放します
4. コードキャッシュの使用
コードキャッシュの使用状況を監視するには、現在使用中のメモリのサイズを追跡する必要があります。
コードキャッシュの使用に関する情報を取得するには、–XX:+PrintCodeCacheJVMオプションを指定できます。 アプリケーションを実行すると、同様の出力が表示されます。
CodeCache: size=32768Kb used=542Kb max_used=542Kb free=32226Kb
これらの各値の意味を見てみましょう。
- 出力のsizeは、メモリの最大サイズを示します。これは、ReservedCodeCacheSizeと同じです。
- used は、現在使用されているメモリの実際のサイズです
- max_used は、使用されている最大サイズです
- free は、まだ使用されていない残りのメモリです
PrintCodeCache オプションは、次のように非常に便利です。
- フラッシングがいつ発生するかを確認します
- クリティカルなメモリ使用ポイントに到達したかどうかを判断します
5. セグメント化されたコードキャッシュ
Java 9の時点で、JVMはコードキャッシュを3つの異なるセグメントに分割し、各セグメントには特定のタイプのコンパイル済みコードが含まれています。 具体的には、次の3つのセグメントがあります。
- 非メソッドセグメントには、バイトコードインタープリターなどのJVM内部関連コードが含まれています。 デフォルトでは、このセグメントは約5MBです。 また、 -XX:NonNMethodCodeHeapSize調整フラグを使用してセグメントサイズを構成することもできます。
- プロファイルコードセグメントには、寿命が短い可能性のある、軽く最適化されたコードが含まれています。 セグメントサイズはデフォルトで約122MBですが、 -XX:ProfiledCodeHeapSize調整フラグを使用して変更できます。
- プロファイルされていないセグメントには、完全に最適化されたコードが含まれており、存続期間が長くなる可能性があります。 同様に、デフォルトでは約122MBです。 もちろん、この値は -XX:NonProfiledCodeHeapSize調整フラグを介して構成できます。
この新しい構造は、さまざまなタイプのコンパイル済みコードを異なる方法で処理するため、全体的なパフォーマンスが向上します。
たとえば、短命のコンパイル済みコードを長命のコードから分離すると、メソッドスイーパーのパフォーマンスが向上します。これは主に、メモリのより小さな領域をスキャンする必要があるためです。
6. 結論
このクイック記事では、JVMコードキャッシュについて簡単に紹介します。
さらに、このメモリ領域を監視および診断するための使用法と調整オプションをいくつか紹介しました。