Penyahpepijatan Jauh Aplikasi Java

1. Gambaran keseluruhan

Menyahpepijat Aplikasi Java yang jauh dapat berguna dalam lebih dari satu kes.

Dalam tutorial ini, kita akan mengetahui cara melakukannya menggunakan perkakas JDK.

2. Permohonan

Mari mulakan dengan menulis permohonan. Kami akan menjalankannya di lokasi terpencil dan menyahpepijatnya secara tempatan melalui artikel ini:

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: Protokol Kabel Debug Java

The Protocol Wire Java Debug adalah protokol yang digunakan dalam Java untuk komunikasi antara debuggee dan penyahpepijat . Debuggee adalah aplikasi yang di-debug sedangkan debugger adalah aplikasi atau proses yang menghubungkan ke aplikasi yang sedang debug.

Kedua-dua aplikasi dijalankan pada mesin yang sama atau pada mesin yang berbeza. Kami akan memberi tumpuan kepada yang terakhir.

3.1. Pilihan JDWP

Kami akan menggunakan JDWP dalam argumen baris perintah JVM semasa melancarkan aplikasi debuggee.

Permintaannya memerlukan senarai pilihan:

  • pengangkutan adalah satu-satunya pilihan yang diperlukan sepenuhnya. Ini menentukan mekanisme pengangkutan yang akan digunakan. dt_shmem hanya berfungsi pada Windows dan jika kedua-dua proses dijalankan pada mesin yang sama sementara dt_socket serasi dengan semua platform dan membolehkan proses berjalan pada mesin yang berbeza
  • pelayan bukan pilihan wajib. Bendera ini, ketika dihidupkan, menentukan cara melekatnya pada penyahpepijat. Ini sama ada memperlihatkan proses melalui alamat yang ditentukan dalam pilihan alamat . Jika tidak, JDWP mendedahkan yang lalai
  • suspend mendefinisikan sama ada JVM harus menangguhkan dan menunggu debugger dilampirkan atau tidak
  • alamat adalah pilihan yang mengandungi alamat, biasanya port, yang terdedah oleh debuggee. Ini juga dapat mewakili alamat yang diterjemahkan sebagai rentetan karakter (seperti javadebug jika kita menggunakan pelayan = y tanpa memberikan alamat pada Windows)

3.2. Lancarkan Perintah

Mari mulakan dengan melancarkan aplikasi jarak jauh. Kami akan memberikan semua pilihan yang disenaraikan sebelumnya:

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

Sehingga Java 5, argumen JVM runjdwp harus digunakan bersama dengan pilihan debug lain :

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

Cara menggunakan JDWP ini masih disokong tetapi akan dikeluarkan dalam siaran masa depan. Kami lebih suka penggunaan notasi yang lebih baru apabila mungkin.

3.3. Sejak Java 9

Akhirnya, salah satu pilihan JDWP telah berubah dengan pelepasan Java versi 9. Ini adalah perubahan kecil kerana ia hanya berkaitan dengan satu pilihan tetapi akan membuat perbezaan jika kita cuba men-debug aplikasi jarak jauh.

Perubahan ini mempengaruhi tingkah laku alamat untuk aplikasi jarak jauh. Alamat notasi lama = 8000 hanya berlaku untuk localhost . Untuk mencapai tingkah laku lama, kita akan menggunakan tanda bintang dengan titik dua sebagai awalan untuk alamat (misalnya alamat = *: 8000 ).

Menurut dokumentasi, ini tidak selamat dan disarankan untuk menentukan alamat IP debugger bila mungkin:

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

4. JDB: Penyahpepijat Java

JDB, Java Debugger, adalah alat yang termasuk dalam JDK yang dirancang untuk menyediakan klien debugger yang mudah dari baris perintah.

Untuk melancarkan JDB, kami akan menggunakan mod lampiran . Mod ini melekatkan JDB ke JVM yang sedang berjalan. Mod berjalan lain ada, seperti mendengar atau menjalankan tetapi kebanyakannya selesa semasa men-debug aplikasi yang dijalankan secara tempatan:

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

4.1. Titik putus

Mari teruskan dengan meletakkan beberapa titik putus dalam aplikasi yang dibentangkan di bahagian 1.

Kami akan menetapkan titik putus pada konstruktor:

> stop in OurApplication. 

Kami akan menetapkan yang lain dalam kaedah utama statik , menggunakan nama kelas String yang layak sepenuhnya :

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

Akhirnya, kami akan menetapkan yang terakhir pada kaedah instance buildInstanceString :

> stop in OurApplication.buildInstanceString(int) 

Kita sekarang harus melihat aplikasi pelayan berhenti dan yang berikut dicetak di konsol debugger kami:

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

Mari sekarang tambahkan titik putus pada baris tertentu, yang mana pemboleh ubah app.instanceString dicetak:

> stop at OurApplication:7 

Kita dapati bahawa di digunakan selepas stop bukannya dalam apabila breakpoint ditakrifkan pada garis tertentu.

4.2. Navigasi dan Nilaikan

Sekarang setelah kita menetapkan titik putus, mari kita gunakan cont untuk meneruskan pelaksanaan utas kita sehingga kita mencapai titik putus pada baris 7.

Kita mesti melihat yang berikut dicetak di konsol:

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

Sebagai peringatan, kami berhenti di baris yang mengandungi kod berikut:

System.out.println(app.instanceString); 

Stopping on this line could have also been done by stopping on the main method and typing step twice. step executes the current line of code and stops the debugger directly on the next line.

Now that we've stopped, the debugee is evaluating our staticString, the app‘s instanceString, the local variable i and finally taking a look at how to evaluate other expressions.

Let's print staticField to the console:

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

We explicitly put the name of the class before the static field.

Let's now print the instance field of app:

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

Next, let's see the variable i:

> print i i = 68741 

Unlike the other variables, local variables don't require to specify a class or an instance. We can also see that print has exactly the same behavior as eval: they both evaluate an expression or a variable.

We'll evaluate a new instance of OurApplication for which we've passed an integer as a constructor parameter:

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

Now that we've evaluated all the variables we needed to, we'll want to delete the breakpoints set earlier and let the thread continue its processing. To achieve this, we'll use the command clear followed by the breakpoint's identifier.

The identifier is exactly the same as the one used earlier with the command stop:

> clear OurApplication:7 Removed: breakpoint OurApplication:7 

Untuk mengesahkan sama ada titik putus telah dikeluarkan dengan betul, kami akan menggunakan jelas tanpa argumen. Ini akan memaparkan senarai titik putus yang ada tanpa yang baru kita hapus:

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

5. Kesimpulan

Dalam artikel ringkas ini, kami telah menemui cara menggunakan JDWP bersama-sama dengan JDB, kedua-dua alat JDK.

Sudah tentu, lebih banyak maklumat mengenai perkakas terdapat dalam rujukan masing-masing: JDWP dan JDB - untuk mengetahui lebih mendalam mengenai perkakas.