Fjernbetjening af Java-applikationer

1. Oversigt

Fejlretning af en ekstern Java-applikation kan være praktisk i mere end et tilfælde.

I denne vejledning opdager vi, hvordan du gør det ved hjælp af JDK's værktøj.

2. Ansøgningen

Lad os starte med at skrive en ansøgning. Vi kører det på en fjernplacering og fejler det lokalt gennem denne artikel:

offentlig klasse OurApplication {private static String staticString = "Statisk streng"; privat strenginstansString; 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); }} offentlig OurApplication (int index) {this.instanceString = buildInstanceString (index); } public String buildInstanceString (int number) {return number + ". Instance String!"; }} 

3. JDWP: Java Debug Wire-protokollen

Det Java Debug Wire-protokoler en protokol, der bruges i Java til kommunikation mellem en debuggee og en debugger. Debuggee er den applikation, der debugges, mens debugger er en applikation eller en proces, der forbinder til den applikation, der debugges.

Begge applikationer kører enten på den samme maskine eller på forskellige maskiner. Vi fokuserer på sidstnævnte.

3.1. JDWP's muligheder

Vi bruger JDWP i JVM-kommandolinjeargumenterne, når vi starter debuggee-applikationen.

Dets påkaldelse kræver en liste med muligheder:

  • transportere er den eneste fuldt ud nødvendige mulighed. Den definerer, hvilken transportmekanisme der skal bruges. dt_shmem fungerer kun på Windows, og hvis begge processer kører på den samme maskine mens dt_socket er kompatibel med alle platforme og tillader processerne at køre på forskellige maskiner
  • server er ikke en obligatorisk mulighed. Dette flag definerer, når det er slået til, den måde, det vedhæftes på debuggeren. Enten udsætter den processen gennem den adresse, der er defineret i adresse mulighed. Ellers udsætter JDWP en standard
  • suspendere definerer, om JVM skal suspendere og vente på, at en debugger tilsluttes eller ej
  • adresse er indstillingen, der indeholder adressen, generelt en port, eksponeret af debuggee. Det kan også repræsentere en adresse oversat som en streng af tegn (som f.eks javadebug hvis vi bruger server = y uden at give en adresse på Windows)

3.2. Start kommando

Lad os starte med at starte fjernapplikationen. Vi giver alle de indstillinger, der er anført tidligere:

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

Indtil Java 5, JVM-argumentet runjdwp skulle bruges sammen med den anden mulighed fejlfinde:

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

Denne måde at bruge JDWP understøttes stadig, men vil blive droppet i fremtidige udgivelser. Vi foretrækker brugen af ​​den nyere notation, når det er muligt.

3.3. Siden Java 9

Endelig er en af ​​mulighederne for JDWP ændret med frigivelsen af ​​version 9 af Java. Dette er en ganske mindre ændring, da den kun vedrører en mulighed, men vil gøre en forskel, hvis vi prøver at fejle en fjernapplikation.

Denne ændring påvirker vejen adresse opfører sig for fjernapplikationer. Den ældre notation adresse = 8000 gælder kun for lokal vært. For at opnå den gamle adfærd bruger vi en stjerne med et kolon som præfiks for adressen (f.eks adresse = *: 8000).

Ifølge dokumentationen er dette ikke sikkert, og det anbefales at angive fejlfindings-IP-adressen, når det er muligt:

java -agentlib: jdwp = transport = dt_socket, server = y, suspendere = n, adresse = 127.0.0.1: 8000

4. JDB: Java-fejlfindingsprogrammet

JDB, Java Debugger, er et værktøj, der er inkluderet i JDK udtænkt til at give en bekvem debugger-klient fra kommandolinjen.

For at starte JDB bruger vi vedhæft mode. Denne tilstand knytter JDB til en kørende JVM. Der findes andre kørefunktioner, f.eks Lyt eller løb men er for det meste bekvemme ved fejlfinding af et lokalt kørende program:

jdb-vedhæft 127.0.0.1:8000> Initialisering af jdb ... 

4.1. Breakpoints

Lad os fortsætte med at sætte nogle breakpoints i applikationen præsenteret i afsnit 1.

Vi sætter et brudpunkt på konstruktøren:

> stop i OurApplication. 

Vi indstiller en anden i den statiske metode vigtigste, ved hjælp af det fuldt kvalificerede navn på Snor klasse:

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

Endelig indstiller vi den sidste på instansmetoden buildInstanceString:

> stop i OurApplication.buildInstanceString (int) 

Vi skal nu bemærke, at serverapplikationen stopper, og følgende udskrives i vores fejlfindingskonsol:

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

Lad os nu tilføje et brudpunkt på en bestemt linje, hvor variablen er app.instanceString udskrives:

> stop ved OurApplication: 7 

Vi bemærker det bruges efter hold op i stedet for i når brudpunktet er defineret på en bestemt linje.

4.2. Naviger og evaluer

Nu hvor vi har indstillet vores breakpoints, lad os bruge det fortsat at fortsætte udførelsen af ​​vores tråd, indtil vi når brudpunktet på linje 7.

Vi skal se følgende udskrevet i konsollen:

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

Som en påmindelse er vi stoppet på linjen, der indeholder følgende kode:

System.out.println (app.instanceString); 

Stop på denne linje kunne også have været gjort ved at stoppe på vigtigste metode og typning trin to gange. trin udfører den aktuelle kodelinje og stopper debuggeren direkte på den næste linje.

Nu hvor vi er stoppet, evaluerer flygtninge vores staticString, det app'S instansString, den lokale variabel jeg og endelig tage et kig på, hvordan man kan evaluere andre udtryk.

Lad os udskrive staticField til konsollen:

> eval OurApplication.staticString OurApplication.staticString = "Statisk streng" 

Vi sætter eksplicit navnet på klassen foran det statiske felt.

Lad os nu udskrive forekomsten af app:

> eval app.instanceString app.instanceString = "68741. Instansstreng!" 

Lad os derefter se variablen jeg:

> udskriv i i = 68741 

I modsætning til de andre variabler kræver lokale variabler ikke at angive en klasse eller en forekomst. Det kan vi også se Print har nøjagtig samme adfærd som eval: begge vurderer et udtryk eller en variabel.

Vi evaluerer en ny forekomst af Vores anvendelse som vi har bestået et heltal for som en konstruktorparameter:

> udskriv ny OurApplication (10) .instanceString ny OurApplication (10) .instanceString = "10. Instance String!" 

Nu hvor vi har evalueret alle de variabler, vi har brug for, vil vi slette de indstillede breakpoints og lade tråden fortsætte behandlingen. For at opnå dette bruger vi kommandoen klar efterfulgt af pausens identifikator.

Identifikatoren er nøjagtig den samme som den, der blev brugt tidligere med kommandoen hold op:

> ryd OurApplication: 7 Fjernet: breakpoint OurApplication: 7 

For at kontrollere, om brudpunktet er korrekt fjernet, bruger vi klar uden argumenter. Dette viser listen over eksisterende breakpoints uden den, vi lige har slettet:

> ryd indstillede breakpoints: breakpoint OurApplication. breakpoint OurApplication.buildInstanceString (int) breakpoint OurApplication.main (java.lang.String []) 

5. Konklusion

I: i denne hurtige artikel har vi opdaget, hvordan man bruger JDWP sammen med JDB, begge JDK-værktøjer.

Mere information om værktøjet kan naturligvis findes i deres respektive referencer: JDWP'er og JDB'er - for at gå dybere ind i værktøjet.