引入:

上文提到了JDI的Mirror机制,把整个目标虚拟机上的所有数据、类型、域、方法、事件、状态和资源,以及调试器发向目标虚拟机的事件请求等都映射成Mirror 对象。这里进一步讨论JDI的链接模块。


分析:

连接模块其主要目的是提供调试器(Debugger)到目标虚拟机(Target VM)之间的交互通道。

从连接的发起方来看:连接的发起方可以是调试器,也可以是目标虚拟机。

从连接的数量来看,一个调试器可以连接多个目标VM, 但是一个目标VM只可以连接一个调试器。


我们从调试器(Debugger)的角度,可以把连接分为主动连接和被动连接。


分类1:主动连接 (它表示调试器主动去连接Target VM)

又分两种情况:

a. 当Target VM还没启动时,则使用LaunchingConnector这种形式的连接器,它会启动目标VM并连接。

Step 1: 调试器调用 VirtualMachineManager 的 launchingConnectors()方法获取所有的LaunchingConnector的实例。

public List launchingConnectors()
  {
    ArrayList list = new ArrayList(2);
    list.add(new SocketLaunchingConnectorImpl(this));
    list.add(new SocketRawLaunchingConnectorImpl(this));
    return list;
  }


Step 2:根据传输方式或其他特征选择一个LaunchingConnector,调用其 launch() 方法启动并且连接目标虚拟机 。启动后,返回目标虚拟机的实例。

比如说,如果选用SocketLaunchingConnectorImpl,则它的launch()方法如下:

public VirtualMachine launch(Map connectionArgs)
    throws IOException, IllegalConnectorArgumentsException, VMStartException
  {
    getConnectionArguments(connectionArgs);

    SocketListeningConnectorImpl listenConnector = new SocketListeningConnectorImpl(
      virtualMachineManager());
    Map args = listenConnector.defaultArguments();
    ((Connector.IntegerArgument)args.get("timeout")).setValue(10000);
    String address = listenConnector.startListening(args);

    String slash = System.getProperty("file.separator");
    String execString = this.fHome + slash + "bin" + slash + this.fLauncher;

    execString = execString + " -Xdebug -Xnoagent -Djava.compiler=NONE";
    execString = execString + " -Xrunjdwp:transport=dt_socket,address=" + address + ",server=n,suspend=" + (this.fSuspend ? "y" : "n");

    if (this.fOptions != null) {
      execString = execString + " " + this.fOptions;
    }

    execString = execString + " " + this.fMain;

    String[] cmdLine = DebugPlugin.parseArguments(execString);
    Process proc = Runtime.getRuntime().exec(cmdLine);
    try
    {
      virtualMachine = (VirtualMachineImpl)listenConnector.accept(args);
    }
    catch (InterruptedIOException localInterruptedIOException)
    {
      VirtualMachineImpl virtualMachine;
      proc.destroy();
      String message = NLS.bind(ConnectMessages.SocketLaunchingConnectorImpl_VM_did_not_connect_within_given_time___0__ms_1, 
        new String[] { 
        ((Connector.IntegerArgument)args
        .get("timeout")).value() });
      throw new VMStartException(message, proc);
    }
    VirtualMachineImpl virtualMachine;
    virtualMachine.setLaunchedProcess(proc);
    return virtualMachine;
  }


b.当Target VM已经启动时,则使用AttachingConnector这种形式的连接器,它会挂接到目标虚拟机上。

前提是,Target VM必须以以下方式启动  -agentlib:jdwp=transport=xxx,server=y 参数启动,并根据传输方式生成监听地址。

Step1:调试器启动,调用 VirtualMachineManager 的 attachingConnectors()  方法获取所有的AttachingConnector的实例。

 public List attachingConnectors()
  {
    ArrayList list = new ArrayList(1);
    list.add(new SocketAttachingConnectorImpl(this));
    return list;


Step 2: 根据目标虚拟机采用的传输方式选择一个AttachingConnector,调用其 attach() 方法挂接到目标虚拟机上。完成连接后,返回目标虚拟机的实例。

 public VirtualMachine attach(Map connectionArgs)
    throws IOException, IllegalConnectorArgumentsException
  {
    getConnectionArguments(connectionArgs);
    Connection connection = null;
    try {
      connection = ((SocketTransportImpl)this.fTransport).attach(this.fHostname, 
        this.fPort, this.fTimeout, 0L);
    } catch (IllegalArgumentException e) {
      List args = new ArrayList();
      args.add("hostname");
      args.add("port");
      throw new IllegalConnectorArgumentsException(e.getMessage(), args);
    }
    return establishedConnection(connection);
  }


分类2:被动连接(它表示Debugger被动地等待或者监听由Target VM发起的连接)

前提是,Target VM必须以以下方式启动  -agentlib:jdwp=transport=xxx,address=yyy 参数启动,并根据传输方式生成监听地址。

Step 1:调试器通过 VirtualMachineManager 的 listeningConnectors() 方法获取所有的ListeningConnector实例。

 public List listeningConnectors()
  {
    ArrayList list = new ArrayList(1);
    list.add(new SocketListeningConnectorImpl(this));
    return list;
  }


Step 2:调用ListeningConnector的 startListening() 方法让连接器进入监听状态。通过 accept() 方法通知连接器开始等待正确的入站链接,该方法将返回调试器正在监听的地址描述符;目标虚拟机会自动地 attach 到调试器上建立连接,然后返回目标虚拟机的实例。

 public String startListening(Map connectionArgs)
    throws IOException, IllegalConnectorArgumentsException
  {
    getConnectionArguments(connectionArgs);
    String result = null;
    try {
      result = ((SocketTransportImpl)this.fTransport).startListening(this.fPort);
    } catch (IllegalArgumentException localIllegalArgumentException) {
      throw new IllegalConnectorArgumentsException(
        ConnectMessages.SocketListeningConnectorImpl_ListeningConnector_Socket_Port, 
        "port");
    }
    return result;
  }