初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为
初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为
今天在项目开发中需要用到socket tcp连接相关(作为tcp客户端),在思考中发觉需要理清socket主动、被动关闭时发生了什么,所以做了一番实验,验证socket tcp连接在调用close、被GC回收、程序运行完毕退出、程序进程被杀掉时,tcp会产生什么行为。得出了一些结论,记录于此同时分享给大家。
先写出得到的结论:
- java程序运行完毕退出和被杀进程时,socket tcp连接会被关闭。而且是通过发送RST方式关闭tcp,不是四次挥手方式关闭tcp,不会进入TIME_WAIT状态。(一般在关闭异常连接时,使用发出RST复位标志的方式)
- 在socket对象被GC回收时,socket的close()方法会被调用,此时将使用四次挥手方式关闭主动tcp连接,随后进入一段时间的TIME_WAIT状态。主动调用socket的close()方法效果相同。
- 在使用new Socket(host,port)创建socket对象后,便会建立起tcp连接。
以上结论是个人测试得到的,如果有出入欢迎指正。
PS:下方是new Socket(host,port)构造方法的代码,结合上面结论第3条,可见在构造方法中第40行进行了一些业务逻辑。联想到前段时间广泛传播的《阿里巴巴JAVA开发手册.pdf》文档,其中有这样一条:
11.【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
可见这个规则也不是处处适用的(至少此处jdk中Socket的构造方法就与这条规则相悖),在使用时考虑到团队协作、符合习惯等时考虑遵守这条规则。
/**
* Creates a stream socket and connects it to the specified port
* number on the named host.
* <p>
* If the specified host is {@code null} it is the equivalent of
* specifying the address as
* {@link java.net.InetAddress#getByName InetAddress.getByName}{@code (null)}.
* In other words, it is equivalent to specifying an address of the
* loopback interface. </p>
* <p>
* If the application has specified a server socket factory, that
* factory's {@code createSocketImpl} method is called to create
* the actual socket implementation. Otherwise a "plain" socket is created.
* <p>
* If there is a security manager, its
* {@code checkConnect} method is called
* with the host address and {@code port}
* as its arguments. This could result in a SecurityException.
*
* @param host the host name, or {@code null} for the loopback address.
* @param port the port number.
*
* @exception UnknownHostException if the IP address of
* the host could not be determined.
*
* @exception IOException if an I/O error occurs when creating the socket.
* @exception SecurityException if a security manager exists and its
* {@code checkConnect} method doesn't allow the operation.
* @exception IllegalArgumentException if the port parameter is outside
* the specified range of valid port values, which is between
* 0 and 65535, inclusive.
* @see java.net.Socket#setSocketImplFactory(java.net.SocketImplFactory)
* @see java.net.SocketImpl
* @see java.net.SocketImplFactory#createSocketImpl()
* @see SecurityManager#checkConnect
*/
public Socket(String host, int port)
throws UnknownHostException, IOException
{
this(host != null ? new InetSocketAddress(host, port) :
new InetSocketAddress(InetAddress.getByName(null), port),
(SocketAddress) null, true);
}
验证时,使用的工具:
- windows cmd命令行,“netstat -ano|findstr 9911”命令(9911是我测试时连接的对方服务器端口),用以查看tcp连接情况,包括tcp是否存在,以及处于TIME_WAIT、ESTABLISHED或者别的状态;
- wireshark,抓包详细查看tcp中传输的数据包;
- JProfiler,java性能分析工具,我在使用时和IDEA做了集成,这里用它的主要目的是手动触发GC,以及验证是否进行了GC。
具体验证过程:
使用以下测试代码
public static void main(String[] args) throws Exception {
main2();
System.out.println("调用结束");
Thread.sleep(15000);
System.out.println("退出程序");
}
public static void main2() throws Exception {
System.out.println("启动");
Thread.sleep(5000);
System.out.println("创建socket对象");
Socket socket = new Socket("123.56.113.123", 9911);
System.out.println("socket.isConnected() = " + socket.isConnected());
System.out.println("socket.isClosed() = " + socket.isClosed());
Thread.sleep(5000);
System.out.println("获取输出流");
OutputStream outputStream = socket.getOutputStream();
System.out.println("socket.isConnected() = " + socket.isConnected());
System.out.println("socket.isClosed() = " + socket.isClosed());
System.out.println("即将退出");
// socket.shutdownOutput();
Thread.sleep(5000);
System.out.println("退出");
}
为了方便在执行不同的操作之间,进行手动的tcp连接情况查询,代码中加了一些sleep()。
测试方案1,程序正常执行,不手动GC,不杀进程:
- 在14行之前,没有创建tcp连接;
- 14行创建了Socket对象之后,tcp连接就已经建立了,此时socket.isConnected()为true,socket.isClosed()为false;
- 19行执行时,没有发现tcp中有任何数据传输,说明获取输出流是不需要网络层的动作的;
- 19行之后,程序退出之前,tcp一直是ESTABLISHED状态(前提是这期间没有发生过GC);
- 程序退出后,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执行结束后便可以被回收。
在测试中,第2、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中有如下方法:
/**
* Cleans up if the user forgets to close it.
*/
protected void finalize() throws IOException {
close();
}
在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
初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为的更多相关文章
- 探究Java中的引用
探究Java中的四种引用 从JDK1.2版本开始,Java把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期.这四种级别由高到低依次为:强引用.软引用.弱引用和虚引用.本篇就来详细探究 ...
- Java中内存泄露及垃圾回收机制
转自:http://blog.sina.com.cn/s/blog_538b279a0100098d.html 写的相当不错滴...................... 摘 要 Java语言中,内 ...
- 2. Java中的垃圾收集 - GC参考手册
标记-清除(Mark and Sweep)是最经典的垃圾收集算法.将理论用于生产实践时, 会有很多需要优化调整的地点, 以适应具体环境.下面通过一个简单的例子, 让我们一步步记录下来, 看看如何才能保 ...
- Java中的分代垃圾回收策略
一.分代GC的理论基础 分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的.因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率. 在Java程序运行的过程中,会产生大 ...
- java内存机制和GC垃圾回收机制
Java 内存区域和GC机制 转载来源于:https://www.cnblogs.com/zhguang/p/3257367.html 感谢 目录 Java垃圾回收概况 Java内存区域 Java对象 ...
- 浅谈Java中的System.gc()的工作原理
很多人把Java的“效率低下”归咎于不能自由管理内存,但我们也知道将内存管理封装起来的好处,这里就不赘述. Java中的内存分配是随着new一个新的对象来实现的,这个很简单,而且也还是有一些可以“改进 ...
- 探究Java中的锁
一.锁的作用和比较 1.Lock接口及其类图 Lock接口:是Java提供的用来控制多个线程访问共享资源的方式. ReentrantLock:Lock的实现类,提供了可重入的加锁语义 ReadWrit ...
- Java中的构造器与垃圾回收
构造器 在我们初始化对象时,如果希望设置一些默认值,那么就可以使用构造器,在Java中,构造器使用和类同名的名字且没有返回值,如下 class Test{ private String name; T ...
- 探究Java中Map类
Map以按键/数值对的形式存储数据,和数组非常相似,在数组中存在的索引,它们本身也是对象. Map的接口 Map---实现Map Map.Entry--Map的内部 ...
随机推荐
- 随机Prim法创建随机迷宫(C#实现)
因为这两天想参加一个比赛,所以就在上网找素材,刚好看到了迷宫生成,就决定拿这个开刀了. 参考的原文地址为(来源页面) 源地址中是使用AS实现的,没学过AS,所以直接不会运行,于是就自己根据原文的概念进 ...
- 【MySql】——MHA+GTID+failover+binlog-server+Atlas
一.环境准备 1.mysql-db01 #系统版本 [root@mysql-db01 ~]# cat /etc/redhat-release CentOS release 6.7 (Final) #内 ...
- WebGIS开源解决方案之环境搭建(二)
续上篇,本文主要介绍开源GIS数据库产品postgres的安装, 从postgis官网下载安装文件,下载地址http://postgis.net 本文一postgresql-9.4.4-3-windo ...
- JS对select动态添加option操作 (三级联动) (搜索拼接)
以下纯属自我理解之下再东搜西查的内容~ JS对select动态添加option操作有个高大上的艺名叫多级联动:第一级改变时,第二级跟着变,第二级改变时,第三级跟着变... 本菜鸟是在工作中遇到做收货地 ...
- 关于爬楼梯的lintcode代码
讲真的,这个我只会用递归去做,但是lintcode上面超时,所以只有在网上找了个动态规划的,虽然这个程序懂了,但是我觉得还是挺不容易的真正弄懂的话-- class Solution {public: ...
- 如何应对苹果app 的ipv6 时代?腾讯专家教您进行环境改造
WeTest 导读 WWDC2015苹果宣布在ios9支持纯IPv6的网络服务,并且要求2016年提交到app store的应用必须兼容纯IPv6的网络,要求适配的系统版本是ios9以上(包括ios9 ...
- POJ1850-Code 递推数学
题目链接:http://poj.org/problem?id=1850 题目大意: 按照字典序对升序排列组成的字母进行编号,给出一个长度不超过10的串,求出它的编号是多少?如果无法进行编号则输出0. ...
- Linux:PS命令详解与使用
要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,ps命令就是最基本进程查看命令.使用该命令可以确定有哪些进程正在运行和运行的状态.进程是否结束.进程有没有僵尸.哪些进程占 ...
- RabbitMQ学习-1补充
1.如果尝试声明一个已经存在的队列会发生什么? 只要参数完全匹配现存的队列的话,Rabbit什么也不做,并返回成功,就好像这个队列已经创建成功. 2.如何检测队列是否存在? 在创建队列的时候设置que ...
- @PathVariable和@RequestParam的区别,@SessionAttributes
简介: handler method参数绑定常用的注解,我们根据他们处理的Request的不同内容部分分为四类: A:处理requet uri部分(这里指uri template中variable,不 ...