引入:
java
上文提到了JDI的Mirror機制,把整個目標虛擬機上的全部數據、類型、域、方法、事件、狀態和資源,以及調試器發向目標虛擬機的事件請求等都映射成Mirror 對象。這裏進一步討論JDI的連接模塊。socket
分析:ide
鏈接模塊其主要目的是提供調試器(Debugger)到目標虛擬機(Target VM)之間的交互通道。this
從鏈接的發起方來看:鏈接的發起方能夠是調試器,也能夠是目標虛擬機。spa
從鏈接的數量來看,一個調試器能夠鏈接多個目標VM, 可是一個目標VM只能夠鏈接一個調試器。debug
咱們從調試器(Debugger)的角度,能夠把鏈接分爲主動鏈接和被動鏈接。調試
分類1:主動鏈接 (它表示調試器主動去鏈接Target VM)server
又分兩種狀況:對象
a. 當Target VM還沒啓動時,則使用LaunchingConnector這種形式的鏈接器,它會啓動目標VM並鏈接。
事件
Step 1: 調試器調用 VirtualMachineManager 的 launchingConnectors()方法獲取全部的LaunchingConnector的實例。
public List<LaunchingConnector> 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<String, ? extends Connector.Argument> 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<AttachingConnector> attachingConnectors() { ArrayList list = new ArrayList(1); list.add(new SocketAttachingConnectorImpl(this)); return list;
Step 2: 根據目標虛擬機採用的傳輸方式選擇一個AttachingConnector,調用其 attach() 方法掛接到目標虛擬機上。完成鏈接後,返回目標虛擬機的實例。
public VirtualMachine attach(Map<String, ? extends Connector.Argument> 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<ListeningConnector> listeningConnectors() { ArrayList list = new ArrayList(1); list.add(new SocketListeningConnectorImpl(this)); return list; }
Step 2:調用ListeningConnector的 startListening() 方法讓鏈接器進入監聽狀態。經過 accept() 方法通知鏈接器開始等待正確的入站連接,該方法將返回調試器正在監聽的地址描述符;目標虛擬機會自動地 attach 到調試器上創建鏈接,而後返回目標虛擬機的實例。
public String startListening(Map<String, ? extends Connector.Argument> 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; }