Connection and Invocation Details


连接和调用详情

目录

传输
连接器
VM调用选项
使用JDB连接
服务提供者接口


传输

Java平台调试器架构(JPDA)传输是调试器和正在被调试的虚拟机(目标VM)之间的通信方法。通信是面向连接的,一侧充当服务器,监听连接;另一侧充当客户端并连接到服务器。JPDA允许调试器应用程序或目标VM充当服务器。传输实现可以允许在单台计算机上运行的进程之间,不同计算机上运行的进程之间,或两者之间进行通信。在建立连接时,使用传输地址来标识连接的端点。传输地址的格式取决于传输类型。

在JPDA中,调试器应用程序使用Java调试接口(JDI)接口和Connector抽象来与目标VM建立连接。请参阅连接器部分。调试器应用程序使用的Connector封装了传输。在目标VM上,支持Java调试线协议的代理用于与调试器通信。这个代理(可能内置在目标VM中或从运行时库加载)封装了与调试器通信的传输。

参考实现中附带了两种传输实现:基于TCP/IP的套接字传输和共享内存传输。规范不要求存在任何特定的传输实现。除了实现中提供的传输之外,该架构还包括服务提供者接口,允许开发和部署其他传输。请参阅服务提供者接口部分。

套接字传输

JPDA参考实现为Linux、macOS和Microsoft Windows平台提供了套接字传输。使用套接字传输,调试器应用程序和目标VM可以位于同一台计算机上,也可以位于不同计算机上。套接字传输在调试器应用程序和目标VM之间使用单个流TCP/IP连接。套接字传输在套接字传输的JDI端和目标VM端上都支持IPv4和IPv6。

命令和回复数据包根据JDWP规范使用JDWP传输接口写入流。由于JDWP可以发送许多小数据包,因此在某些套接字实现中,通过避免在发送之前缓冲小数据包而引起的延迟,TCP_NO_DELAY套接字选项可以提高性能。套接字会优雅地关闭,以便尽可能发送未发送的数据。

套接字传输通过唯一字符串dt_socket进行标识。此名称可用于在调用目标VM时选择套接字传输。请参阅VM调用选项部分。在调试器应用程序中,使用封装了套接字传输的相应Connector

套接字传输地址的格式为<host>:<port>,其中<host>是主机名或IP地址(可能用方括号括起来),<port>是要连接或监听的套接字端口号。如果<host>为空,则使用本地回环地址。如果<host>在服务器等待客户端连接的上下文中等于"*",则服务器会监听所有网络接口。

共享内存传输

除了套接字传输外,JPDA参考实现还在Windows上提供了共享内存传输。共享内存传输使用共享内存区域在调试器应用程序和目标VM之间交换JDWP数据包。使用共享内存传输,调试器应用程序和目标VM必须位于同一台计算机上。

共享内存传输通过唯一字符串dt_shmem进行标识。此名称可用于在调用目标VM时选择共享内存传输。请参阅VM调用选项部分。在调试器应用程序中,使用封装了共享内存传输的相应Connector

共享内存传输地址只是可以用作Windows文件映射对象名称的名称字符串。名称字符串可以由任何字符组合组成,不包括反斜杠。

连接器

连接器是用于在调试器应用程序(编写到JDI)和目标VM之间建立连接的JDI抽象。不同的JDI实现可以自由提供不同的连接器实现,以匹配它们支持的传输和VM。连接器接口非常通用,这允许JDI与不同的连接器实现一起使用。连接器通过一组名称-值对进行配置。特定的连接器接受不同的名称-值对。

一个良好的JDI客户端应用程序允许用户选择和配置可能存在的任何连接器,但将特定连接器的知识整合到调试器中可以使其配置更加愉快。JPDA提供的示例JDB实现说明了这种方法。请参阅使用JDB连接部分。

JDI参考实现提供了几个连接器,这些连接器映射到可用的传输类型和连接模式(启动、监听和附加)。这些连接器在以下部分中进行描述。通过JDI方法VirtualMachineManager.allConnectors()返回包含这些连接器的List。此外,每个附加、监听和启动连接器都包含在相应的VirtualMachineManager方法attachingConnectors()listeningConnectors()launchingConnectors()返回的列表中。

命令行启动连接器

调试器应用程序可以使用此连接器启动支持与描述在VM调用选项部分中描述的相同调试选项的任何VM。启动VM和指定必要的调试选项的详细信息由连接器处理。此连接器使用的底层传输取决于平台。在Windows上,使用共享内存传输。在Linux和macOS上,使用套接字传输。

此连接器通过名称com.sun.jdi.CommandLineLaunch唯一标识。


命令行启动连接器参数
名称 是否必需? 默认值 描述
home 当前java.home属性值 用于调用目标VM的Java运行时环境(JRE)的位置。
options "" 除了标准调试选项之外,用于调用VM的选项。请参阅VM调用选项
main "" 被调试应用程序的主类和命令行参数。
suspend true 如果目标VM在加载主类之前立即暂停,则为true;否则为false。
quote "\"" 用于在命令行上组合以空格分隔的文本的字符。
vmexec "java" VM启动器可执行文件。如果可用,可以将其更改为javawjava_g以进行调试。

原始命令行启动连接器

调试器应用程序可以使用此连接器启动任何VM。必须指定整个命令行,并且不会对其进行任何编辑。此连接器使用的底层传输取决于平台。在Windows上,使用共享内存传输。在Linux和macOS上,使用套接字传输。

此连接器通过名称com.sun.jdi.RawCommandLineLaunch唯一标识。


原始命令行启动连接器参数
名称 是否必需? 默认值 描述
command "" 用于以要调试的应用程序启动目标VM的完整命令行。
address "" 用于监听新启动的目标VM连接的传输地址。请参阅传输部分。此值通常也是原始命令行参数的一部分,但如果目标VM有其他确定应连接到的传输地址的方法,则不需要。
quote "\"" 用于在命令行上组合以空格分隔的文本的字符。

套接字附加连接器

调试器应用程序可以使用此连接器通过套接字传输附加到当前运行的目标VM。目标VM必须已经使用与此连接器描述的参数一致的选项启动。请参阅VM调用选项部分了解所需选项。

此连接器通过名称com.sun.jdi.SocketAttach唯一标识。


Socket Attaching Connector Arguments
名称 是否必需 默认值 描述
hostname 本地主机名 要连接的主机机器的名称。
port "" 要连接的host机器上的端口号。
timeout "" 连接到目标VM时要使用的超时时间(以毫秒为单位)。

共享内存连接器

此连接器可供调试器应用程序通过共享内存传输连接到当前运行的目标VM。仅在Windows上可用。目标VM必须已经使用与此连接器参数一致的选项启动。部分VM调用选项描述了所需的选项。

此连接器的唯一标识符为com.sun.jdi.SharedMemoryAttach


共享内存连接器参数
名称 是否必需 默认值 描述
name "" 目标VM正在侦听的共享内存传输地址。请参阅传输部分。
timeout "" 连接到目标VM时要使用的超时时间(以毫秒为单位)

Socket监听连接器

此连接器可供调试器应用程序通过套接字传输接受来自单独调用的目标VM的连接。目标VM必须使用与此连接器参数一致的选项启动。部分VM调用选项描述了所需的选项。

此连接器可以接受来自多个目标VM的连接。

此连接器的唯一标识符为com.sun.jdi.SocketListen


Socket监听连接器参数
名称 是否必需 默认值 描述
port 临时端口号(由TCP/IP堆栈分配的端口) 用于侦听连接的端口号
localAddress 回环地址 用于侦听连接的主机名或IP地址
timeout "" 等待目标VM连接时要使用的超时时间(以毫秒为单位)

共享内存监听连接器

此连接器可供调试器应用程序通过共享内存传输接受来自单独调用的目标VM的连接。仅在Windows上可用。目标VM必须使用与此连接器参数一致的选项启动。部分VM调用选项描述了所需的选项。

此连接器可以接受来自多个目标VM的连接。

此连接器的唯一标识符为com.sun.jdi.SharedMemoryListen


共享内存监听连接器参数
名称 是否必需 默认值 描述
name "" 用于侦听目标VM连接的共享内存传输地址
timeout "" 等待目标VM连接时要使用的超时时间(以毫秒为单位)

进程连接器

此连接器可供调试器应用程序连接到使用在VM调用选项中描述的server=y调试子选项启动的当前运行的目标VM。目标VM必须是Java SE 6或更新版本。

进程连接器没有关联的传输。相反,传输在实际连接发生时动态确定。因此,对于此连接器,transport().name()方法返回local

此连接器的唯一标识符为com.sun.jdi.ProcessAttach

进程连接器参数
名称 是否必需 默认值 描述
pid "" 要调试的进程的进程ID。
timeout "" 连接到目标VM时要使用的超时时间(以毫秒为单位)。



VM调用选项

本节描述了调试时调用VM所需的选项。

Oracle的VM实现需要命令行选项来加载用于调试的JDWP代理。-agentlib:jdwp选项用于加载并指定JDWP代理的选项。

-agentlib:jdwp选项的指定如下:

-agentlib:jdwp=<子选项>
加载JDWP的JPDA参考实现。此库驻留在目标VM中,并使用Java虚拟机工具接口(JVM TI)和Java本机接口(JNI)与其交互。它使用传输和JDWP协议与单独的调试器应用程序通信。部分-agentlib:jdwp和-Xrunjdwp子选项描述了具体的子选项。

-agentlib:jdwp和-Xrunjdwp子选项

-agentlib:jdwp-Xrunjdwp选项可以进一步使用子选项进行限定。子选项的指定如下:

    -agentlib:jdwp=<名称1>[=<值1>],<名称2>[=<值2>]...

or

    -Xrunjdwp:<名称1>[=<值1>],<名称2>[=<值2>]...

以下表格描述了可用的选项:

-Xrunjdwp 子选项
名称 是否必需? 默认值 描述
help N/A 打印简要帮助信息并退出虚拟机。
transport none 用于连接到调试器应用程序的传输名称。
server "n"

如果为"y",则监听调试器应用程序以附加;否则,在指定地址附加到调试器应用程序。

如果为"y"且未指定地址,则选择一个传输地址来监听调试器应用程序,并将地址打印到标准输出流。参见章节传输

address 是,如果server=n,否则否 "" 连接的传输地址。如果server=n,则尝试在此地址附加到调试器应用程序。如果server=y,则在此地址监听连接。参见章节传输
timeout "" 如果server=y,指定等待调试器附加的超时时间(毫秒)。如果server=n,指定附加到调试器时要使用的超时时间(毫秒)。请注意,某些传输实现可能会忽略超时选项。
launch none

在JDWP初始化完成时,启动给定字符串中的进程。此选项与onthrow和/或onuncaught结合使用,提供“即时调试”,即在发生特定事件时启动调试器进程。

请注意,启动的进程不会在自己的窗口中启动。在大多数情况下,启动的进程应该是一个小型应用程序,它反过来启动调试器应用程序在自己的窗口中。

以下字符串附加到此参数中给定的字符串(以空格分隔)。它们可以帮助启动的调试器建立与此虚拟机的连接。执行生成的字符串。

  • transport子选项的值。
  • address子选项的值(或者如果未给出,则生成的地址)
onthrow none 延迟JDWP库的初始化,直到在此虚拟机中抛出给定类的异常。异常类名必须是包限定的。连接建立包含在JDWP初始化中,因此直到抛出异常才会开始。
onuncaught "n" 如果为"y",则延迟JDWP库的初始化,直到在此虚拟机中抛出未捕获的异常。连接建立包含在JDWP初始化中,因此直到抛出异常才会开始。请参见JDI规范中的com.sun.jdi.ExceptionEvent以获取未捕获异常的定义。
suspend "y" 如果为"y",VMStartEvent的suspendPolicy为SUSPEND_ALL。如果为"n",VMStartEvent的suspendPolicy为SUSPEND_NONE。
includevirtualthreads "n" 如果为"y",当调试器请求所有运行线程的列表时,包括虚拟线程。在附加到调试器之前创建的虚拟线程可能不会包括在内。如果虚拟线程数量非常大,这可能会使调试器不堪重负。如果为"y",还会导致JDWP库记住所有创建的虚拟线程,直到它们死亡,如果数量很大,这可能会使JDWP库不堪重负。

此外,dt_socket传输支持以下选项:

allow "*"

如果server=y,则仅允许从指定的地址/子网连接。

该值可以是"*"(允许从任何地址连接),或由加号(+)分隔的地址列表。

列表中的每个条目可以是:

  • 单个IP地址(例如,127.0.0.1或::1)。

  • 由子网地址和前缀长度定义的地址组(子网),例如,192.168.1.0/24定义地址范围192.168.1.0 - 192.168.1.255。

    对于IPv4,前缀长度必须在1到31的范围内;对于IPv6,范围是1到127。

示例

-agentlib:jdwp=transport=dt_socket,server=y,address=8000
在回环地址上的端口8000上监听套接字连接。在主类加载之前暂停此虚拟机(默认情况下为suspend=y)。一旦调试器应用程序连接,它可以发送JDWP命令以恢复虚拟机。

-agentlib:jdwp=transport=dt_socket,server=y,address=*:8000,allow=192.168.1.0/24+::1,timeout=5000
在所有网络接口上的端口8000上监听套接字连接。仅允许调试器从地址192.168.1.00 - 192.168.1.255和本地机器的IPv6地址(::1)连接。如果调试器在5秒内未附加,则终止。在主类加载之前暂停此虚拟机(默认情况下为suspend=y)。一旦调试器应用程序连接,它可以发送JDWP命令以恢复虚拟机。

-agentlib:jdwp=transport=dt_shmem,server=y,suspend=n
选择一个可用的共享内存传输地址并将其打印到标准输出流。在该地址监听共享内存连接。允许虚拟机在调试器应用程序附加之前开始执行。

-agentlib:jdwp=transport=dt_socket,address=myhost:8000
通过主机myhost上端口8000的套接字附加到正在运行的调试器应用程序。在主类加载之前暂停此虚拟机。

-agentlib:jdwp=transport=dt_shmem,address=mysharedmemory
通过传输地址mysharedmemory的共享内存附加到正在运行的调试器应用程序。在主类加载之前暂停此虚拟机。

-agentlib:jdwp=transport=dt_socket,server=y,address=192.168.1.18:8000,allow=*,onthrow=java.io.IOException,launch=/usr/local/bin/debugstub
等待在此虚拟机中抛出java.io.IOException的实例。暂停虚拟机(默认情况下为suspend=y)。在地址192.168.1.18的端口8000上监听套接字连接。允许调试器从任何地址连接。执行以下操作:" /usr/local/bin/debugstub dt_socket myhost:8000"。此程序可以在单独的窗口中启动调试器进程,该进程将附加到此虚拟机并开始调试它。

-agentlib:jdwp=transport=dt_shmem,server=y,onuncaught=y,launch=d:\bin\debugstub.exe
等待在此虚拟机中抛出未捕获的异常。暂停虚拟机。选择一个共享内存传输地址并在该地址监听连接。执行以下操作:" d:\bin\debugstub.exe dt_shmem <address>",其中<address>是所选的共享内存地址。此程序可以在单独的窗口中启动调试器进程,该进程将附加到此虚拟机并开始调试它。

使用JDB连接

Java调试器(JDB)的示例实现提供了JPDA的JDI连接器使用示例。JDB具有对其已知连接器(即,参考实现中存在的连接器)的“快捷”选项。它还提供了一种使用任何连接器建立通用连接的方法。虽然JDB很难算是一个良好的调试器界面示例,但它提供了连接器使用的简单示例。

在JDB中,-attach选项提供对参考实现中的一个附加连接器的访问(在Windows上为共享内存,在Linux和macOS上为套接字)。-listen选项提供对参考实现中的一个监听连接器的访问(在Windows上为共享内存,在Linux和macOS上为套接字)。在命令行上直接指定的类名和参数提供对命令行启动连接器的访问。

例如:

jdb -attach myhost:8000

是附加到具有套接字附加连接器的目标虚拟机的简单方法(在Linux或macOS上),而

jdb Hello 1 2 3

是使用命令行启动连接器启动目标虚拟机的简单方法。

但是,JDB还提供了-connect选项来处理任何连接器,它接受连接器名称和一组任意名称-值参数对。例如,前面的命令行有以下等效项:

jdb -connect com.sun.jdi.SocketAttach:hostname=myhost,port=8000
jdb -connect "com.sun.jdi.CommandLineLaunch:main=Hello 1 2 3"

这些命令行比上面的命令行更繁琐,但-connect选项可以与任何连接器一起使用。这种操作是JDI调试器如何处理任何类型的连接器的原始示例,同时提供了一个简化的界面来处理常见的、众所周知的连接器。


服务提供者接口

JPDA包括服务提供者接口,允许开发和部署连接器和传输实现。这些服务提供者接口允许调试器和其他工具供应商开发新的连接器实现,并提供超出Oracle提供的套接字和共享内存传输之外的传输机制。JDI中的服务提供者接口在com.sun.jdi.connect.spi包中指定。

除了JDI中的服务提供者接口外,Oracle实现还包括一个称为Java调试线协议传输接口的传输库接口。传输库由目标虚拟机中的JDWP代理加载,并用于建立与调试器的连接以及在调试器和虚拟机之间传输JDWP数据包。

有关更多信息,请参见Java平台调试器体系结构 - 服务提供者接口