JDB 快速指南



JDB - 简介

调试是一个技术过程,用于查找并移除程序中的错误或缺陷,并获得预期的结果。调试包括测试和监控。当程序的子单元紧密耦合时,调试会变得非常复杂。我们可以使用遵循规定的 API 的调试器工具来调试程序。调试器允许您逐步浏览代码的每个方面,检查所有元素,并消除任何错误。

调试技术

有不同类型的技术可以用来调试 Java 程序。旧的调试方法是使用打印语句在每个段的末尾,这将在控制台上打印跟踪语句。请查看以下代码。

pubic class Add
{
   public static void main(String ar[])
   {
      int a=ar[0];
      system.out.println("A : " +a);
      int b=ar[1];
      system.out.println("B : " +b);
      int c = a + b;
      system.out.println("C = a + b : " +c);
   }
}

这里,我们有一个程序,它将两个数字相加并打印输出。请注意,在每个步骤中,我们都引入了一个打印语句,该语句在控制台上打印程序的状态。这是调试程序的传统方法。

此外,我们还有可以用来调试程序的高级概念,例如

  • 单步执行
  • 断点,以及
  • 异常或监视点。

调试类型

我们可以使用多种方法调试程序

  • 使用 Java 字节码(Java 代码的编译版本)
  • 在程序内部使用注释
  • 将类附加到正在运行的程序
  • 远程调试
  • 按需调试
  • 优化代码调试

Java 调试器

以下是一些市面上可用的 Java 调试器的示例

  • IDE(如 Eclipse、Netbeans 等)包含它们自己的调试器(Visual cafe、Borland、JBuilder)
  • 独立调试器 GUI(如 Jikes、Java 平台调试器和 JProbe)
  • 命令行调试器(Sun 的 JDB)
  • Notepad 或 VI 驱动(堆栈跟踪)

本教程介绍如何使用命令行调试器jdb

JDB

Java 调试器 (JDB) 是一个用于 Java 类的工具,用于在命令行中调试程序。它实现了 Java 平台调试器架构。它有助于使用 Java 调试接口 (JDI) 检测和修复 Java 程序中的错误。

JDK 中的 JDB

以下架构定义了 JDB 在 JDK 中的角色。它主要包含三个单元

  • Java 虚拟机工具接口 (JVM TI)
  • Java 调试线池 (JDWP)
  • Java 调试接口 (JDI)
JDB Architecture

JVM TI

它是由 VM 实现的本机编程接口。它提供了检查和调试在 VM 上运行的应用程序状态的方法。它允许实现者(VM 实现者)可以轻松地封装到调试架构中。它还使用名为JDWP的第三方通道进行通信。

JDWP

它定义了在被调试进程和调试器前端之间传递的信息和请求的格式。拥有 JDWP 的主要目的是允许被调试器和调试器在它们在单独的 VM 或单独的平台上运行时进行通信。

JDI

它是一个作为前端实现的高级 Java 接口。它定义了用户代码级别上的变量信息。建议对所有调试器开发使用 JDI 层。它使用 JDWP 与被调试的 JVM 通信。

JDB - 安装

本章说明如何在基于 Windows 和 Linux 的系统上安装 JDB。JDB 是 JDK 的一部分。因此,JDK 安装足以在命令提示符下使用 JDB。

系统要求

以下是安装 JDB 的系统要求

JDK Java SE 2 JDK 1.5 或更高版本
内存 1 GB RAM(推荐)
磁盘空间 无最低要求
操作系统版本 Windows XP 或更高版本,Linux

按照以下给出的简单步骤在您的系统上安装 JDB。

步骤 1:验证 Java 安装

首先,您需要在您的系统上安装 Java 软件开发工具包 (SDK)。要验证这一点,请根据您正在使用的平台执行以下两个命令中的任何一个。

如果 Java 安装已正确完成,则它会显示 Java 安装的当前版本和规范。示例输出在以下表格中给出。

平台 命令 示例输出
Windows

打开命令控制台并键入

\>java –version

Java 版本 "1.7.0_60"

Java (TM) SE 运行时环境 (build 1.7.0_60-b19)

Java Hotspot (TM) 64 位服务器 VM (build 24.60-b09,混合模式)

Linux

打开命令终端并键入

$java –version

java 版本 "1.7.0_25"

Open JDK 运行时环境 (rhel-2.3.10.4.el6_4-x86_64)

Open JDK 64 位服务器 VM (build 23.7-b01,混合模式)

我们假设本教程的读者在其系统上安装了 Java SDK 版本 1.7.0_60。如果您没有 Java SDK,请从链接http://www.oracle.com/technetwork/java/javase/downloads/index.html下载其当前版本并安装它。

步骤 2:设置 Java 环境

设置环境变量 JAVA_HOME 以指向 Java 安装在您计算机上的基本目录位置。例如,

平台 描述
Windows 将 JAVA_HOME 设置为 C:\ProgramFiles\java\jdk1.7.0_60
Linux export JAVA_HOME=/usr/local/java

将 Java 编译器位置的完整路径附加到系统路径。

平台 描述
Windows 在系统变量 PATH 的末尾附加字符串 "C:\Program Files\Java\jdk1.7.0_60\bin"。
Linux export PATH=$PATH:$JAVA_HOME/bin/

从命令提示符执行命令java -version,如上所述。

步骤 3:验证 JDB 安装

如下验证 JDB 版本

平台 命令 示例输出
Windows

打开命令控制台并键入

\>jdb –version

这是 JDB 版本 1.6(Java SE 版本 1.7.0_60)
Linux

打开命令终端并键入

$jdb –version

这是 JDB 版本 1.6(Java SE 版本 1.7.0_60)

JDB - 语法

本章解释了 JDB 命令的语法。语法包含以下列出的四个部分

  • JDB
  • 选项
  • 参数

语法

JDB 的语法如下。

jdb [ options ] [ class ] [ arguments ]

JDB

它从 Java 开发工具包调用 jdb.exe。

选项

这些包括用于以有效方式调试 Java 程序的命令行选项。JDB 启动器接受所有选项(例如 -D、-classpath 和 -X)以及一些其他高级选项(例如 (-attach、-listen、-launch 等)。

它是您要对其执行调试操作的类名。

参数

这些是在运行时给定给程序的输入值。例如,arg[0]、arg[1] 到 main() 方法。

在以上四个部分中,选项是最重要的一个。

JDB - 选项

本章描述了 JDB 中可用的重要选项,这些选项作为参数与 jdb 命令一起提交。

选项

下表包含 JDB 接受的选项列表

名称 描述
-help 显示帮助消息并列出相关选项。
-sourcepath 如果未指定路径,则使用给定路径作为源文件,否则它将采用默认路径“.”,即当前目录。
-attach 通过指定正在运行的 VM 地址将调试器附加到正在运行的 VM。
-listen 等待正在运行的 VM 使用标准连接器连接。
-listenany 等待正在运行的 VM 使用任何地址连接。
-launch 在启动作业时立即启动被调试的应用程序。
-listconnectors 列出此 VM 中可用的连接器。
-connect 使用命名连接器和列出的参数值连接到目标 VM。
-dbgtrace 打印用于调试 jdb 的信息。
-tclient 在 Java Hotspot VM(客户端)中运行应用程序。
-tserver 在 Java Hotspot VM(服务器)中运行应用程序。
-Joption 将选项传递给用于运行 JDB 的 Java 虚拟机。

将选项与命令一起使用

以下命令显示如何使用上述一些选项

-help

以下命令获取有关使用 JDB 的 -help。

\>jdb -help

-attach

以下命令将调试器附加到指定的 VM(端口号:1099)。

\> jdb -attach 1099

-listen

以下命令使 JDB 进程在当前 VM 上使用标准连接器等待(VM 在 8008)。

\>jdb -listen 8088

-listenany

以下命令使 JDB 进程在当前 VM 上使用任何连接器等待(VM 在当前运行的端口)。

\>jdb –listenany

-tclient

以下命令在 Java Hotspot(™) VM(客户端)中执行应用程序。

\>jdb –tclient

-tserver

以下命令在 Java Hotspot(™) VM(服务器)中执行应用程序。

\>jdb -tserver

JDB - 会话

本章介绍如何以不同的方式启动 JDB 会话。JDB 启动是启动 JDB 会话的常用技术。

有两种不同的方法可以启动 JDB 会话

  • 通过向其中添加类(主类名)来启动 JDB 会话。
  • 将 JDB 添加到正在运行的 JVM 以启动会话。

通过添加类启动会话

以下命令启动 JDB 会话

语法

\>jdb <classname>

示例

假设我们有一个名为TestClass的类。以下命令从 TestClass 启动 JDB 会话。

\>jdb TestClass

如果您遵循此命令,它将使用任何指定的参数启动一个新的 Java VM。然后它加载类并在执行类的第一个语句之前停止它。

通过将 JDB 添加到正在运行的 JVM 启动会话

下面是通过将 JDB 添加到正在运行的 JVM 启动 JDB 会话的语法和示例。

语法

以下语法适用于 JDB 会话

-agentlib:jdwp=transport=dt_shmem,address=,server=y,suspend=n

示例

假设主类名是TestClass,并且 JVM 允许 JDB 稍后连接它。以下是将 JDB 添加到 JVM 的命令

\>java
-agentlib:jdwp=transport=dt_shmem,address=jdbconn,server=y,suspend=n TestClass

现在您可以使用以下命令将 JDB 附加到 JVM

\> jdb -attach jdbconn

注意:此处,TestClass未添加到 JDB 命令中,因为 JDB 连接到正在运行的 VM 而不是启动一个新的 VM。

JDB - 基本命令

本章将引导您了解 JDB 的基本命令。启动会话后,这些命令用于调试程序。

以下是用于调试的命令列表。

名称 描述
help 或 ? 最重要的JDB命令;它显示已识别命令的列表以及简要说明。
run 在启动JDB并设置必要的断点后,您可以使用此命令启动执行并调试应用程序。
cont 在断点、异常或单步执行后继续执行调试的应用程序。
print 显示 Java 对象和基本值。
dump 对于基本值,此命令与 print 相同。对于对象,它会打印对象中定义的每个字段的当前值。包括静态和实例字段。
threads 列出当前正在运行的线程。
thread 选择一个线程作为当前线程。
where 转储当前线程的堆栈。

示例

让我们假设对于以下示例,我们有一个名为**Add**的示例类

Add.java

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

使用以下命令编译此类 Add.java

\>javac Add.java

Run

此命令执行主类文件,该文件已添加到 JDB 以进行调试。执行以下命令以运行 Add 类。

\>jdb Add
initializing jdb …
>run

执行这些命令后,您将看到以下输出

Basic Commands

JDB - 断点

本章介绍断点的概念以及如何在程序中设置断点。断点在调试期间会在程序执行的特定代码行处引入显式停止或暂停。它有助于在程序执行过程中获取有关程序中变量的知识。

语法

以下命令在特定行号处设置断点

> stop at <class name>:<Line no>

以下命令在特定方法或特定变量上设置断点

> stop in <class name>:< Method name | Variable name>

示例

以下示例演示如何在类中设置断点。

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

将以上文件保存为 Add.java。使用以下命令编译此文件

\>javac Add.java

调试

让我们以调试为例。在这里,我们通过在 main() 上设置断点来启动调试过程。以下是调试过程中需要遵循的步骤

步骤 1:启动 JDB 会话

以下命令在 Add 类上启动 JDB 会话以进行调试

\> jdb Add

步骤 2:设置断点

以下命令在 Add 类的 main() 方法上设置断点。

> stop in Add.main

如果断点设置成功,您将看到以下输出

Deferring breakpoint Add.main.
It will set after the class is loaded.
>

步骤 3:开始调试

以下命令开始执行类 Add

> run Add

如果运行此命令,您将看到以下输出。在此输出中,您会发现执行在断点位置停止,即在 main() 函数处。

Breakpoints

执行在 main 方法的第一行停止,即在“int a=5, b=6;”或代码中的第 11 行。您可以在输出中观察到此信息。

步骤 4:继续执行

以下命令继续程序执行

cont

它为您提供其余的执行部分和输出,如下所示

> Add:11
The application exited
\>

JDB - 单步执行

本章介绍如何在调试程序中使用单步执行的概念。单步执行是调试器功能,允许您逐行执行代码。使用此功能,您可以检查代码的每一行以确保它们按预期运行。

以下命令用于单步执行过程

  • step:单步执行到下一行
  • list:检查您在代码中的位置
  • cont:继续执行剩余部分

示例

以下示例使用我们在上一章中使用的 Add 类

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

将以上文件保存为 Add.java。使用以下命令编译此文件

\>javac Add.java

让我们假设断点已设置在 Add 类的 main() 方法上。以下步骤演示如何在 Add 类中应用单步执行。

步骤 1:执行作业

以下命令开始执行名为 Add 的类。

> run Add

如果执行此命令,您将看到以下输出。在此输出中,您可以发现执行在断点位置停止,即在 main() 方法处。

Stepping1

执行在 main 方法的第一行停止,即在“int a=5, b=6;”或代码中的第 11 行。您可以在输出中观察到此信息。

步骤 2:单步执行代码

以下命令将执行单步执行到下一行。

main[1] step

现在执行单步执行到第 12 行。您将看到以下输出。

Stepping2

步骤 3:列出代码

以下命令列出代码

main[1] list

您将获得以下输出。list 命令用于让您知道程序控制已到达的代码行。请注意以下屏幕截图中的箭头标记=>,它显示了程序控制的当前位置。

Stepping3

步骤 4:继续执行

以下命令继续执行代码

main[1] cont

此命令继续执行代码的其余行。输出如下所示

> Add:11
The application exited
\>

通常,单步执行有三种类型

  • Step Into(步入)
  • Step Over(步过)
  • Step Out(步出)

Step Into(步入)

使用此命令,您可以单步执行到代码的下一行。如果代码的下一行是函数调用,则它会通过将控制驱动到函数的顶行来进入该函数。

在以下代码中,箭头标记定义了代码中的控制器。

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      -> Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

如果使用**step into**命令,控制器将移动到下一行,即“int c = ob.addition(a,b);”。在此行中,有一个函数调用**addition(int, int)**,因此控制器将移动到 addition 函数的最顶行,箭头标记如下所示

public class Add
{
   public int addition( int x, int y)
   -> {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      int c = ob.addition(a,b);
   System.out.println("Add: "+c);
   }
}

Step Over(步过)

Step Over 也执行下一行。但是,如果下一行是函数调用,它会在后台执行该函数并返回结果。

让我们举个例子。在以下代码中,箭头标记定义了代码中的控制。

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      -> Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

如果使用**step over**命令,控制器将移动到下一行,即“int c = ob.addition(a,b);”。在此行中,有一个函数调用**addition(int, int)**,因此函数执行在后台完成,结果将返回到当前行,箭头标记如下所示

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      -> int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

Step Out(步出)

Step Out 执行下一行。如果下一行是函数调用,它会跳过该调用,并且函数执行将继续执行代码的其余行。

让我们举个例子。在以下代码中,箭头标记定义了代码中的控制器。

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      -> Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

如果使用**step out**命令,控制器将移动到下一行,即“int c = ob.addition(a,b);”。在此行中,有一个函数调用**addition(int, int)**,因此函数执行被跳过,其余执行将继续执行,箭头标记如下所示

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      -> int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

JDB - 异常

本章介绍如何使用 JDB 处理异常类。通常,每当程序引发没有 catch 语句的异常时,VM 都会打印异常行、异常原因并退出。如果异常已使用 catch 语句引发,则异常将由 catch 语句处理。在这里,VM 会打印带有异常原因的输出。

当引发异常的类在 JDB 下运行时,它也会抛出**未捕获的**异常。可以使用**catch**命令处理该异常。

示例

让我们以 JdbException 类为例

public class JdbException
{
   public static void main(String ar[]) throws Exception
   {
      int a=8, b=0;
      System.out.println("Welcome");
      System.out.println("Ex: "+(a/b));
   }
}

将以上文件保存为 JdbException.java。使用以下命令编译此文件

\>javac JdbException.java

按照以下步骤处理异常。

步骤 1:运行类

以下命令执行名为**JdbException**的类,如下所示

\>jdb JdbException
>run

此**JdbException**类包含异常,因此您将看到以下输出

Exception1

步骤 2:捕获异常

以下命令捕获异常

mian[1] catch java.lang.ArithmeticException

它将为您提供以下输出

Set all java.lang.ArithmeticException

步骤 3:继续执行

以下命令继续执行。现在 catch 处理算术异常,如下所示

Exception2

JDB - 在 Eclipse 中

本章介绍如何在 Eclipse 中使用 JDB。在继续之前,您需要安装 Eclipse Indigo。请按照以下步骤在您的系统上安装 Eclipse Indigo。

步骤 1:下载并安装 Eclipse

您可以从以下链接下载 Eclipse:http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-developers/indigosr2

步骤 2:创建新项目和新类

  • 按照**文件 -> 新建 -> Java 项目**选项创建新的 Java 项目。
  • 将其命名为**“sampledebug”**。
  • 右键单击**samplebebug**项目创建新类。
  • 选择**选项 -> 新建 -> 类**
  • 将其命名为**“Add.java”**

Add.java

public class Add
{
   public int addition( int x, int y)
   {
      int z = x+y;
      return z;
   }
   public static void main( String ar[ ] )
   {
      int a = 5, b = 6;
      Add ob = new Add();
      int c = ob.addition(a,b);
      System.out.println("Add: "+c);
   }
}

步骤 3:打开调试透视图

按照以下说明打开调试透视图。

在 Eclipse IDE 上,转到**窗口 -> 打开透视图 -> 调试**。现在您将获得程序 Add.java 的调试透视图。您将看到以下窗口。

Debug Perspective

调试透视图中的部分

调试透视图中的部分如下所示

编码部分

此部分显示 Java 代码。这是您要调试的代码,即**Add.java**。在这里,我们可以通过双击代码行前面的位置在该行上添加断点。您会发现带有箭头符号的蓝色圆圈来指出该行的断点。请参阅以下屏幕截图;您可以找到用红色圆圈标记的选定区域“1”。

  1. 在此处双击。您可以为该行设置断点。
Code Section

断点部分

此部分定义了设置为程序代码的断点列表。在这里,我们可以添加、删除、查找和管理断点。以下屏幕截图显示了断点部分。

Breakpoint Section

观察给定屏幕截图中的以下选项

  1. 使用左侧的复选框,我们可以选择或取消选择断点。在这里,我们使用一个断点,即 Add 类-main() 方法。

  2. 单个叉子图标“X”用于删除选定的断点。

  3. 双叉子图标“XX”用于删除代码中的所有断点。

  4. 箭头指针用于指向应用选定断点的代码。

断点部分中的其余功能如下所示

  • 命中次数:它显示控制到达此断点的次数。它用于递归逻辑。

  • 挂起线程:我们可以通过选择它来挂起当前线程。

  • 挂起 VM:我们可以通过选择它来挂起 VM。

调试部分

此部分用于调试过程。它包含调试中使用的选项。

开始调试:请按照以下说明开始调试。

右键单击代码 ->单击作为调试 ->单击1 Java 应用程序

调试过程开始,如下面的屏幕截图所示。它包含一些选定的选项,使用数字突出显示。

  1. 我们在 Add 类 main() 方法上应用了断点。当我们开始调试时,控制器将停留在 main() 方法的第一行。

  2. 用于恢复调试过程并跳过当前断点。其工作方式类似于 JDB 命令行中的cont命令。

  3. 用于停止调试过程。

  4. 其工作方式类似于 JDB 命令行中的step in过程。用于将控制权移至下一行,即点“1”移至下一行。

  5. 其工作方式类似于 JDB 命令行中的step over过程。

  6. 用于查看断点应用在哪一行。

Debug Section

按照给定的步骤和部分在 eclipse IDE 中调试代码。默认情况下,每个 IDE 都包含此调试过程。

广告