初步探究java中程序退出、GC垃圾回收時,socket tcp鏈接的行爲

初步探究java中程序退出、GC垃圾回收時,socket tcp鏈接的行爲html

今天在項目開發中須要用到socket tcp鏈接相關(做爲tcp客戶端),在思考中發覺須要理清socket主動、被動關閉時發生了什麼,因此作了一番實驗,驗證socket tcp鏈接在調用close、被GC回收、程序運行完畢退出、程序進程被殺掉時,tcp會產生什麼行爲。得出了一些結論,記錄於此同時分享給你們。java

先寫出獲得的結論:windows

  1. java程序運行完畢退出和被殺進程時,socket tcp鏈接會被關閉。並且是經過發送RST方式關閉tcp,不是四次揮手方式關閉tcp,不會進入TIME_WAIT狀態。(通常在關閉異常鏈接時,使用發出RST復位標誌的方式)
  2. 在socket對象被GC回收時,socket的close()方法會被調用,此時將使用四次揮手方式關閉主動tcp鏈接,隨後進入一段時間的TIME_WAIT狀態。主動調用socket的close()方法效果相同。
  3. 在使用new Socket(host,port)建立socket對象後,便會創建起tcp鏈接。

 

以上結論是我的測試獲得的,若是有出入歡迎指正。服務器

PS:下方是new Socket(host,port)構造方法的代碼,結合上面結論第3條,可見在構造方法中第40行進行了一些業務邏輯。聯想到前段時間普遍傳播的《阿里巴巴JAVA開發手冊.pdf》文檔,其中有這樣一條:網絡

11.【強制】構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在 init 方法中。 app

可見這個規則也不是到處適用的(至少此處jdk中Socket的構造方法就與這條規則相悖),在使用時考慮到團隊協做、符合習慣等時考慮遵照這條規則。jvm

 1     /**
 2      * Creates a stream socket and connects it to the specified port
 3      * number on the named host.
 4      * <p>
 5      * If the specified host is {@code null} it is the equivalent of
 6      * specifying the address as
 7      * {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
 8      * In other words, it is equivalent to specifying an address of the
 9      * loopback interface. </p>
10      * <p>
11      * If the application has specified a server socket factory, that
12      * factory's {@code createSocketImpl} method is called to create
13      * the actual socket implementation. Otherwise a "plain" socket is created.
14      * <p>
15      * If there is a security manager, its
16      * {@code checkConnect} method is called
17      * with the host address and {@code port}
18      * as its arguments. This could result in a SecurityException.
19      *
20      * @param      host   the host name, or {@code null} for the loopback address.
21      * @param      port   the port number.
22      *
23      * @exception  UnknownHostException if the IP address of
24      * the host could not be determined.
25      *
26      * @exception  IOException  if an I/O error occurs when creating the socket.
27      * @exception  SecurityException  if a security manager exists and its
28      *             {@code checkConnect} method doesn't allow the operation.
29      * @exception  IllegalArgumentException if the port parameter is outside
30      *             the specified range of valid port values, which is between
31      *             0 and 65535, inclusive.
32      * @see        java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
33      * @see        java.net.SocketImpl
34      * @see        java.net.SocketImplFactory#createSocketImpl()
35      * @see        SecurityManager#checkConnect
36      */
37     public Socket(String host, int port)
38         throws UnknownHostException, IOException
39     {
40         this(host != null ? new InetSocketAddress(host, port) :
41              new InetSocketAddress(InetAddress.getByName(null), port),
42              (SocketAddress) null, true);
43     }

 

驗證時,使用的工具:socket

  • windows cmd命令行,「netstat -ano|findstr 9911」命令(9911是我測試時鏈接的對方服務器端口),用以查看tcp鏈接狀況,包括tcp是否存在,以及處於TIME_WAIT、ESTABLISHED或者別的狀態;
  • wireshark,抓包詳細查看tcp中傳輸的數據包;
  • JProfiler,java性能分析工具,我在使用時和IDEA作了集成,這裏用它的主要目的是手動觸發GC,以及驗證是否進行了GC。

具體驗證過程:tcp

使用如下測試代碼ide

 1     public static void main(String[] args) throws Exception {
 2         main2();
 3         System.out.println("調用結束");
 4         Thread.sleep(15000);
 5         System.out.println("退出程序");
 6 
 7     }
 8 
 9     public static void main2() throws Exception {
10 
11         System.out.println("啓動");
12         Thread.sleep(5000);
13         System.out.println("建立socket對象");
14         Socket socket = new Socket("123.56.113.123", 9911);
15         System.out.println("socket.isConnected() = " + socket.isConnected());
16         System.out.println("socket.isClosed() = " + socket.isClosed());
17         Thread.sleep(5000);
18         System.out.println("獲取輸出流");
19         OutputStream outputStream = socket.getOutputStream();
20         System.out.println("socket.isConnected() = " + socket.isConnected());
21         System.out.println("socket.isClosed() = " + socket.isClosed());
22         System.out.println("即將退出");
23 //        socket.shutdownOutput();
24         Thread.sleep(5000);
25         System.out.println("退出");
26 
27     }

爲了方便在執行不一樣的操做之間,進行手動的tcp鏈接狀況查詢,代碼中加了一些sleep()。

測試方案1,程序正常執行,不手動GC,不殺進程:

  1. 在14行以前,沒有建立tcp鏈接;
  2. 14行建立了Socket對象以後,tcp鏈接就已經創建了,此時socket.isConnected()爲true,socket.isClosed()爲false;
  3. 19行執行時,沒有發現tcp中有任何數據傳輸,說明獲取輸出流是不須要網絡層的動做的;
  4. 19行以後,程序退出以前,tcp一直是ESTABLISHED狀態(前提是這期間沒有發生過GC);
  5. 程序退出後,tcp鏈接立刻消失(沒有進入TIME_WAIT狀態),經過抓包查看是主動發出了RST(發出RST reset復位標誌,通常是關閉異常tcp鏈接的方法)。

 

如下wireshark抓包的截圖中序號爲1-4的包,是上述過程產生的。

測試方案2,tcp鏈接創建以後,程序退出前,殺掉進程:

和方案1中的效果相同,上圖中5-8號包是方案2產生的。殺掉進程時,會主動發出RST關閉tcp鏈接。

測試方案3,tcp鏈接創建後,GC回收socket對象:

觸發GC的方法,是使用JProfiler的Run GC按鈕,觸發GC,以下圖,內存變化說明確實進行了GC。

具體測試的設計,socket對象是main2方法中的局部變量,在main2執行結束後即可以被回收。

在測試中,第二、3行代碼執行完以後,我點擊Run GC按鈕,預計socket對象會被回收。

此時用「netstat -ano|findstr 9911」命令查看,以下圖,tcp已經由ESTABLISHED狀態變爲TIME_WAIT狀態。

直到程序結束退出,仍然是TIME_WAIT狀態。(正常狀況下,TIME_WAIT狀態會持續MSL時間的2倍,即報文最大生存時間的2倍,Windows下默認爲4分鐘)

實際上,在GC時會調用socket的close()方法,致使主動關閉tcp,進入TIME_WAIT狀態。參考 

java - If the jvm gc an unclosed socket instance what would happen to the underlying tcp connection? - Stack Overflow
https://stackoverflow.com/questions/25543149/if-the-jvm-gc-an-unclosed-socket-instance-what-would-happen-to-the-underlying-tc

 在Socket的成員變量中有SocketImpl類型的變量impl,impl實例化的實現類都是直接或間接繼承java.net.AbstractPlainSocketImpl的,AbstractPlainSocketImpl中有以下方法:

1     /**
2      * Cleans up if the user forgets to close it.
3      */
4     protected void finalize() throws IOException {
5         close();
6     }

在GC時,impl的finalize方法會被調用,這時候就至關於調用了socket.close(),因此tcp被正常地主動關閉(socket.close()內的代碼也是調用impl.close(),只是額外加了線程同步控制代碼)

 測試方案4,主動調用socket.close()關閉tcp鏈接:

即將第23行代碼取消註釋,替換爲socket.close(),或socket.shutdownOutput() ,測試結果均和方案3的一致,close或shutdownOutput以後tcp關閉,端口進入TIME_WAIT狀態。

 

以上是本次測試驗證的過程,對java程序退出、GC回收socket對象、被殺進程、主動close時對tcp影響的初步探究,若有錯誤、疏漏還請斧正。

 

轉載請註明出處:http://www.cnblogs.com/zhangdong92/p/7056539.html

相關文章
相關標籤/搜索