
HotSpot JVMには、仮想マシンで何が起こっているかを追跡するための多くのオプションがあります:
PrintGC
、
PrintCompilation
、
TraceClassLoading
など。 通常、これらはコマンドラインオプションに含まれています(例
-XX:+PrintGCDetails
この記事から次のことを学びます。
- すべてのJVMフラグを見つける場所、およびそれらがどのタイプに分割されているか。
- JMXを使用してプログラムでフラグを読み取りまたは設定する方法。
- 仮想マシンのメモリ内で適切な領域を見つけて、それを
台無しにして修正する方法。
HotSpot JVMのフラグは何ですか
説明付きのすべてのフラグのリストは、OpenJDKソースで利用できます: globals.hppの主要部分と、 アーキテクチャ 、 コンパイラ 、および
ご覧のとおり、フラグはさまざまなマクロによって定義されています。
- productおよびproduct_rwフラグは、-XXスイッチを使用してコマンドラインで設定できます。
- 公式リリースではJDKは定数であるため、 developmentとnotproductは面白くありません。
- 管理可能なフラグは、JMXを介して実行時に変更できます。
- Experimentalは公式にはサポートされていません(特にテストが不十分なため)が、自己責任で含めることができます。 これらのフラグを変更するには、たとえば、コマンドラインキー
UnlockExperimentalVMOptions
を追加する必要があります。-XX:+UnlockExperimentalVMOptions -XX:+TrustFinalNonStaticFields
- 診断は 、仮想マシンの問題を調査する目的を除いて使用することを目的としていません。
UnlockDiagnosticVMOptions
と組み合わせてのみ有効にできます。たとえば、-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
JVMのバージョンで使用可能なすべてのフラグとその現在の値を表示するには、
PrintFlagsFinal
パラメーターを指定してJVMを実行する必要があります。
java -XX:+PrintFlagsFinal
JMXを介してフラグを操作する
HotSpotを使用すると、 Management APIを介していくつかのフラグの値をプログラムで読み取ったり設定したりできます。 さらに、 リモート管理を有効にすると、リモートサーバー上でもこれを実行できます。
まず、
com.sun.management:type=HotSpotDiagnostic
type=HotSpotDiagnosticという名前のMXBeanインスタンスを取得する必要があり
com.sun.management:type=HotSpotDiagnostic
。
import com.sun.management.HotSpotDiagnosticMXBean; import java.lang.management.ManagementFactory; import javax.management.MBeanServer; ... MBeanServer server = ManagementFactory.getPlatformMBeanServer(); HotSpotDiagnosticMXBean bean = ManagementFactory.newPlatformMXBeanProxy( server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
bean.getVMOption(String option)
メソッドは、JVMオプションの現在の値を
bean.getVMOption(String option)
ます。
and
bean.setVMOption(String option, String newValue)
-新しいものを設定します。
フラグを読み取ることができる場合は、
manageable
可能なもののみを変更できます。
bean.getDiagnosticOptions()
メソッドは、
manageable
すべてのオプションのリストを返します。
例:
// JVM, ReentrantLock .. thread dump bean.setVMOption("PrintConcurrentLocks", "true");
JVMダイレクトメモリアクセス
残念ながら、JMXを介して変更されるオプションのセットはわずかです。 ただし、JVMフラグは、プロセスのアドレス空間にある単なる普通の変数です。 変数のアドレスがわかっている場合は、 Unsafe APIを使用して変数から新しい値を書き込むことができます 。 JVMフラグのアドレスを見つけることは残っています。 起動から起動まで、アドレスはオペレーティングシステムの意志で変わるため、タスクは簡単ではありません。 幸いなことに、Linuxは非常に適応性の高いOSであり、正しく尋ねられれば、必要な情報をすべて提供してくれます。
- 最初に、
libjvm.so
仮想マシンライブラリのlibjvm.so
、ロードされているアドレスを確認する必要があります。 proc仮想ファイルシステムは、特に、現在のプロセスの仮想アドレス空間のすべての領域をリストするfile/proc/self/maps
に役立ちます。 その中に/libjvm.so
終わる行が/libjvm.so
ます。
2b6707956000-2b67084b8000 r-xp 00000000 68:02 1823284 /usr/java/jdk1.7.0_40/jre/lib/amd64/server/libjvm.so
最初の番号(0x2b6707956000)は、ライブラリがロードされるベースアドレスになります。
これらはすべて純粋なJavaで実行できることに注意してください!
private String findJvmMaps() throws IOException { BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps")); try { for (String s; (s = reader.readLine()) != null; ) { if (s.endsWith("/libjvm.so")) { return s; } } throw new IOException("libjvm.so not found"); } finally { reader.close(); } }
- 重要な瞬間が
libjvm.so
:libjvm.so
ファイルを読み取り用に開きます。 オープンソースのELFパーサーを使用して、ライブラリ内にシンボルセクションがあり、すべてのJVMフラグがシンボル間に存在します。 繰り返しますが、ハッキングはなく、純粋なJavaのみです。
ElfReader elfReader = new ElfReader(jvmLibrary); ElfSymbolTable symtab = (ElfSymbolTable) elfReader.section(".symtab");
- 手順1で取得したライブラリのベースアドレスをシンボルアドレスに追加し、フラグ値が格納されているメモリ内の目的の場所を取得します。
Unsafe.putInt
を使用して簡単に変更できます。
private ElfSymbol findSymbol(String name) { for (ElfSymbol symbol : symtab) { if (name.equals(symbol.name()) && symbol.type() == ElfSymbol.STT_OBJECT) { return symbol; } } throw new NoSuchElementException("Symbol not found: " + name); } public void setIntFlag(String name, int value) { ElfSymbol symbol = findSymbol(name); unsafe.putInt(baseAddress + symbol.value(), value); } public void setBooleanFlag(String name, boolean value) { setIntFlag(name, value ? 1 : 0); }
おわりに
ご覧のとおり、Javaでは、1行のネイティブコードなしで、仮想マシン自体を含むランタイム環境を制御できます。 ただし、文書化されていないメソッドを使用するのは危険であり、本番環境での使用を推奨するものではありません。
実験の完全なソースコードはGitHubにあります 。
詳細については、10月15日にサンクトペテルブルグで開催されるJoker Javaテクノロジー会議に参加してください。 Odnoklassnikiから、JVMを含む3つのレポートが提示されます。