1. 概要

リモートJavaアプリケーションのデバッグは、複数の場合に便利です。

このチュートリアルでは、JDKのツールを使用してこれを行う方法を説明します。

2. アプリケーション

アプリケーションを書くことから始めましょう。 リモートの場所で実行し、次の記事を通じてローカルでデバッグします。

public class OurApplication {
    private static String staticString = "Static String";
    private String instanceString;

    public static void main(String[] args) {
        for (int i = 0; i < 1_000_000_000; i++) {
            OurApplication app = new OurApplication(i);
            System.out.println(app.instanceString);
        }
    }

    public OurApplication(int index) {
        this.instanceString = buildInstanceString(index);
    }

    public String buildInstanceString(int number) {
        return number + ". Instance String !";
    }
}

3. JDWP:JavaDebugWireプロトコル

Java Debug Wire Protocol は、デバッグ対象者とデバッガー間の通信にJavaで使用されるプロトコルです。 debuggeeはデバッグ中のアプリケーションであり、デバッガーはデバッグ中のアプリケーションまたはアプリケーションに接続しているプロセスです。

両方のアプリケーションは、同じマシンまたは異なるマシンで実行されます。 後者に焦点を当てます。

3.1. JDWPのオプション

debuggeeアプリケーションを起動するときは、JVMコマンドライン引数でJDWPを使用します。

その呼び出しには、オプションのリストが必要です。

  • トランスポートは、完全に必要な唯一のオプションです。 使用するトランスポートメカニズムを定義します。 dt_shmemはWindowsでのみ機能し、両方のプロセスが同じマシンで実行される場合 dt_socketはすべてのプラットフォームと互換性があり、プロセスを異なるマシンで実行できます
  • serverは必須オプションではありません。 このフラグがオンの場合、デバッガーに接続する方法を定義します。 addressオプションで定義されたアドレスを介してプロセスを公開します。 それ以外の場合、JDWPはデフォルトのものを公開します
  • suspend は、JVMを一時停止して、デバッガーが接続するのを待つかどうかを定義します
  • address は、デバッグ対象によって公開されるアドレス(通常はポート)を含むオプションです。 また、文字列として変換されたアドレスを表すこともできます(Windowsでアドレスを指定せずに server = y を使用する場合は、 javadebug のように)

3.2. 起動コマンド

まず、リモートアプリケーションを起動します。 前述のすべてのオプションを提供します。

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 OurApplication

Java 5までは、JVM引数runjdwpを他のオプションdebugと一緒に使用する必要がありました。

java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

JDWPを使用するこの方法は引き続きサポートされていますが、将来のリリースで廃止される予定です。 可能な場合は、新しい表記法を使用することをお勧めします。

3.3. Java9以降

最後に、Javaのバージョン9のリリースに伴い、JDWPのオプションの1つが変更されました。 これは1つのオプションのみに関係するため、非常に小さな変更ですが、リモートアプリケーションをデバッグしようとしている場合は違いが生じます。

この変更は、リモートアプリケーションでのアドレスの動作に影響を与えます。 古い表記address= 8000 は、localhostにのみ適用されます。 古い動作を実現するために、アドレスのプレフィックスとしてコロンを含むアスタリスクを使用します(例: address = *:8000 )。

ドキュメントによると、これは安全ではないため、可能な限りデバッガのIPアドレスを指定することをお勧めします。

java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:8000

4. JDB:Javaデバッガー

Java DebuggerであるJDBは、コマンドラインから便利なデバッガクライアントを提供するために考案されたJDKに含まれているツールです。

JDBを起動するには、attachモードを使用します。 このモードは、実行中のJVMにJDBを接続します。 listenrunなどの他の実行モードもありますが、ローカルで実行されているアプリケーションをデバッグする場合に最も便利です。

jdb -attach 127.0.0.1:8000
> Initializing jdb ...

4.1. ブレークポイント

セクション1で示したアプリケーションにいくつかのブレークポイントを設定して続行しましょう。

コンストラクターにブレークポイントを設定します。

> stop in OurApplication.<init>

String クラスの完全修飾名を使用して、静的メソッドmainに別の名前を設定します。

> stop in OurApplication.main(java.lang.String[])

最後に、インスタンスメソッドbuildInstanceStringに最後のものを設定します。

> stop in OurApplication.buildInstanceString(int)

サーバーアプリケーションが停止し、デバッガーコンソールに次のように出力されていることに気付くはずです。

> Breakpoint hit: "thread=main", OurApplication.<init>(), line=11 bci=0

次に、変数app.instanceStringが出力されている特定の行にブレークポイントを追加しましょう。

> stop at OurApplication:7

ブレークポイントが特定の行に定義されている場合、inの代わりにstopの後にatが使用されていることがわかります。

4.2. ナビゲートして評価する

ブレークポイントを設定したので、 cont を使用して、7行目のブレークポイントに到達するまでスレッドの実行を続けましょう。

コンソールに次のように表示されます。

> Breakpoint hit: "thread=main", OurApplication.main(), line=7 bci=17

念のため、次のコードを含む行で停止しました。

System.out.println(app.instanceString);

この行で停止するには、 main メソッドで停止し、stepを2回入力することもできます。 step は、現在のコード行を実行し、次の行でデバッガーを直接停止します。

停止したので、デバッグ対象者は staticString appinstanceString、ローカル変数 i 、そして最後に評価しています。他の式を評価する方法を見てみましょう。

staticFieldをコンソールに出力してみましょう。

> eval OurApplication.staticString
OurApplication.staticString = "Static String"

静的フィールドの前にクラスの名前を明示的に置きます。

appのインスタンスフィールドを出力してみましょう。

> eval app.instanceString
app.instanceString = "68741. Instance String !"

次に、変数iを見てみましょう。

> print i
i = 68741

他の変数とは異なり、ローカル変数はクラスやインスタンスを指定する必要はありません。 また、printevalとまったく同じ動作をしていることがわかります。どちらも、式または変数を評価します。

コンストラクターパラメーターとして整数を渡したOurApplicationの新しいインスタンスを評価します。

> print new OurApplication(10).instanceString
new OurApplication(10).instanceString = "10. Instance String !"

必要なすべての変数を評価したので、前に設定したブレークポイントを削除して、スレッドに処理を続行させます。 これを実現するには、コマンドclearの後にブレークポイントの識別子を使用します。

識別子は、コマンドstopで以前に使用したものとまったく同じです。

> clear OurApplication:7
Removed: breakpoint OurApplication:7

ブレークポイントが正しく削除されたかどうかを確認するために、引数なしでclearを使用します。 これにより、削除したブレークポイントを除いた既存のブレークポイントのリストが表示されます。

> clear
Breakpoints set:
        breakpoint OurApplication.<init>
        breakpoint OurApplication.buildInstanceString(int)
        breakpoint OurApplication.main(java.lang.String[])

5. 結論

I:この簡単な記事では、両方のJDKツールであるJDBと一緒にJDWPを使用する方法を発見しました。

もちろん、ツールの詳細については、それぞれのリファレンスJDWPのおよびJDBのを参照してください。ツールについて詳しく説明しています。