当套接字被解除引用时,它们是否被垃圾收集

时间:2016-06-01 18:35:25

标签: java multithreading sockets networking garbage-collection

我试图理解SO_TIMEOUT,线程资源和垃圾收集的概念。

我在项目中看到了一个编码错误,其中套接字被错误地解除引用而没有关闭。

他们在几分钟内用JAVA 1.6关闭,但是使用JAVA 1.8,他们需要几个小时才能关闭。 [仅在linux平台上测试]

所以我想深入研究这个问题。

我创建了这个程序,我在其中创建了10个套接字但是你可以看到它们中有9个是derefernced然后线程进入长睡眠持续时间:

    import java.net.Socket;

/**
 * Created by 212477559 on 5/30/16.
 */
public class TestClientSocket {

    public static void main(String args[])
    {
        try
        {
            Socket socket;
            for(int j=0;j<10;j++)
            {
                socket =new Socket("localhost",9090);
                socket.setKeepAlive(false);
            }


            Thread.sleep(900000);

        }
       catch(Exception e)
        {
            System.out.println("throws");
            e.printStackTrace();
        }
    }
}

现在我跑

  

netstat -np TCP |找到“9090”

我仍然可以看到,大约半小时后,所有套接字仍处于建立状态。所以我有以下问题:

1)为什么那些9个插座没有被垃圾收集?

2)该线程仍然持有资源吗?

3)以下提到的方法是关闭套接字的唯一方法:

   1. close the sockets?
   2. Thread which created those sockets must exit?
   3. SO_TIMEOUT should expire?

3 个答案:

答案 0 :(得分:3)

当本机资源由java对象表示时,可以在资源保持分配时对对象进行垃圾回收。为了提供安全网,java对象可以覆盖其finalize()方法以实现释放资源。

我假设在具体套接字实现中的某个地方有一个finalize,在这里进行处理,虽然我无法快速浏览一下这个地方。

finalize的问题是,当实际 收集了对象时,它只执行 ,而不是当对象符合GC 时。在您的示例代码中,您的套接字符合GC的条件,但由于您所做的只是休眠,因此VM无需实际执行GC。

编辑:我没有强调或提及的一点,因为对我来说很自然:正确使用套接字的方式(或者实际上任何获取操作系统级资源的方法)是遵循资源提供的close()/ dispose()协议。只有在完成后显式关闭资源,才能确保资源被释放。在JVM堆和垃圾收集经过精细调整的情况下,可能无法长时间收集对象(天或,可能从不)。

答案 1 :(得分:2)

垃圾收集与这些套接字仍处于打开状态无关。

只是简单地

  

垃圾收集是查看堆内存的过程,   识别正在使用哪些对象以及哪些对象未被删除   未使用的对象。使用中的对象或引用的对象是指   程序的某些部分仍然保持指向它的指针   宾语。不再使用未使用的对象或未引用的对象   由程序的任何部分引用。所以内存使用的是   可以回收未引用的对象。

现在,虽然可以回收Socket对象使用的内存,但这并不意味着Sockets的操作系统句柄会随着JVM回收内存的过程而被破坏(这两个是分开的)的东西)。

这就是为套接字提供close方法的原因。这是保证操作系统释放套接字句柄的唯一方法。

套接字应始终在不再需要时关闭。

  

他们在几分钟内用JAVA 1.6关闭,但使用JAVA 1.8   他们需要几个小时才能完成。

如果它们关闭,这甚至不应成为讨论点。

答案 2 :(得分:0)

我深入研究了这个问题,找到了一个有趣的答案,@ Durandal和@Michael Markidis也暗示了这个答案。 发生的事情是由于我的代码中的编码错误,该对象是受到尊重的。 现在的问题是为什么这个解除引用的对象没有关闭java 1.8中的套接字,而是关闭java 1.6。

我发现的是

  • 默认情况下,我的系统中的java 1.6仅为行程空间分配了12 MB
  • 默认情况下,我的系统中的java 1.8为行程空间分配了50 MB

由于存储空间很小,我的旧版本开始快速填充,导致java 1.6中频繁的主要垃圾收集。 但是在java 1.8中,主要的垃圾收集发生在数小时之后,之后我的插座被关闭了。

因此,由于diff JVM版本分配NEW_RATIO和SURVIVOR_RATIO的行为发生了变化,因此发现了该问题。 当我在java 1.8 (在java 1.6中分配)中使用相同的NEW_RATIO和SURVIVOR_RATIO进行测试时,我可以看到我的套接字经常关闭。