8195059: Update java.net Socket and DatagramSocket implementations to use Cleaner
authorrriggs
Fri, 02 Feb 2018 14:17:07 -0500
changeset 48737 7c12219870fd
parent 48736 0454688cc319
child 48738 cdd3239a2a61
8195059: Update java.net Socket and DatagramSocket implementations to use Cleaner Reviewed-by: chegar, plevart
make/mapfiles/libnet/mapfile-vers
src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java
src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java
src/java.base/share/classes/java/net/ServerSocket.java
src/java.base/share/classes/java/net/SocketCleanable.java
src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
src/java.base/unix/classes/java/io/FileDescriptor.java
src/java.base/unix/native/libnet/SocketImpl.c
src/java.base/windows/classes/java/io/FileDescriptor.java
src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java
src/java.base/windows/native/libnet/SocketImpl.c
test/jdk/java/net/DatagramSocket/SendSize.java
test/jdk/java/net/DatagramSocket/UnreferencedDatagramSockets.java
test/jdk/java/net/MulticastSocket/UnreferencedMulticastSockets.java
test/jdk/java/net/ServerSocket/UnreferencedSockets.java
--- a/make/mapfiles/libnet/mapfile-vers	Fri Feb 02 10:32:59 2018 -0800
+++ b/make/mapfiles/libnet/mapfile-vers	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -89,6 +89,7 @@
 		Java_java_net_PlainDatagramSocketImpl_setTimeToLive;
                 Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0;
                 Java_java_net_AbstractPlainDatagramSocketImpl_isReusePortAvailable0;
+                Java_java_net_SocketCleanable_cleanupClose0;
                 Java_jdk_net_Sockets_isReusePortAvailable0;
 		Java_sun_net_PortConfig_getUpper0;
 		Java_sun_net_PortConfig_getLower0;
--- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,11 @@
 
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.security.AccessController;
-import sun.net.ResourceManager;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.Set;
-import java.util.HashSet;
-import java.util.Collections;
+
+import sun.net.ResourceManager;
 import sun.security.action.GetPropertyAction;
 
 /**
@@ -115,6 +115,7 @@
         fd = new FileDescriptor();
         try {
             datagramSocketCreate();
+            SocketCleanable.register(fd);
         } catch (SocketException ioe) {
             ResourceManager.afterUdpClose();
             fd = null;
@@ -265,6 +266,7 @@
      */
     protected void close() {
         if (fd != null) {
+            SocketCleanable.unregister(fd);
             datagramSocketClose();
             ResourceManager.afterUdpClose();
             fd = null;
@@ -275,11 +277,6 @@
         return (fd == null) ? true : false;
     }
 
-    @SuppressWarnings("deprecation")
-    protected void finalize() {
-        close();
-    }
-
     /**
      * set a value - since we only support (setting) binary options
      * here, o must be a Boolean
--- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,17 +25,18 @@
 
 package java.net;
 
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.FileDescriptor;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 import sun.net.ConnectionResetException;
 import sun.net.NetHooks;
 import sun.net.ResourceManager;
-import java.util.Set;
-import java.util.HashSet;
-import java.util.Collections;
 
 /**
  * Default Socket Implementation. This implementation does
@@ -136,6 +137,7 @@
             fd = new FileDescriptor();
             try {
                 socketCreate(false);
+                SocketCleanable.register(fd);
             } catch (IOException ioe) {
                 ResourceManager.afterUdpClose();
                 fd = null;
@@ -144,6 +146,7 @@
         } else {
             fd = new FileDescriptor();
             socketCreate(true);
+            SocketCleanable.register(fd);
         }
         if (socket != null)
             socket.setCreated();
@@ -643,14 +646,6 @@
         socketSendUrgentData (data);
     }
 
-    /**
-     * Cleans up if the user forgets to close it.
-     */
-    @SuppressWarnings("deprecation")
-    protected void finalize() throws IOException {
-        close();
-    }
-
     /*
      * "Acquires" and returns the FileDescriptor for this impl
      *
@@ -748,6 +743,7 @@
      * Close the socket (and release the file descriptor).
      */
     protected void socketClose() throws IOException {
+        SocketCleanable.unregister(fd);
         socketClose0(false);
     }
 
--- a/src/java.base/share/classes/java/net/ServerSocket.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/share/classes/java/net/ServerSocket.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -549,6 +549,7 @@
             si.address = new InetAddress();
             si.fd = new FileDescriptor();
             getImpl().accept(si);
+            SocketCleanable.register(si.fd);   // raw fd has been set
 
             SecurityManager security = System.getSecurityManager();
             if (security != null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/net/SocketCleanable.java	Fri Feb 02 14:17:07 2018 -0500
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.net;
+
+import jdk.internal.misc.JavaIOFileDescriptorAccess;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.ref.PhantomCleanable;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.ref.Cleaner;
+
+
+/**
+ * Cleanup for a socket/datagramsocket FileDescriptor when it becomes phantom reachable.
+ * Create a cleanup if the raw fd != -1. Windows closes sockets using the fd.
+ * Subclassed from {@code PhantomCleanable} so that {@code clear} can be
+ * called to disable the cleanup when the socket fd is closed by any means
+ * other than calling {@link FileDescriptor#close}.
+ * Otherwise, it would incorrectly close the handle or fd after it has been reused.
+ */
+final class SocketCleanable extends PhantomCleanable<Object> {
+
+    // Access to FileDescriptor internals
+    private static final JavaIOFileDescriptorAccess fdAccess =
+            SharedSecrets.getJavaIOFileDescriptorAccess();
+
+    // Native function to call NET_SocketClose(fd)
+    private static native void cleanupClose0(int fd) throws IOException;
+
+    // The raw fd to close
+    private final int fd;
+
+    /**
+     * Register a socket specific Cleanable with the FileDescriptor
+     * if the FileDescriptor is non-null and the raw fd is != -1.
+     *
+     * @param fdo the FileDescriptor; may be null
+     */
+    static void register(FileDescriptor fdo) {
+        if (fdo != null) {
+            int fd = fdAccess.get(fdo);
+            if (fd != -1) {
+                fdAccess.registerCleanup(fdo,
+                        new SocketCleanable(fdo, CleanerFactory.cleaner(), fd));
+            }
+        }
+    }
+
+    /**
+     * Unregister a Cleanable from the FileDescriptor.
+     * @param fdo the FileDescriptor; may be null
+     */
+    static void unregister(FileDescriptor fdo) {
+        if (fdo != null) {
+            fdAccess.unregisterCleanup(fdo);
+        }
+    }
+
+    /**
+     * Constructor for a phantom cleanable reference.
+     *
+     * @param obj     the object to monitor
+     * @param cleaner the cleaner
+     * @param fd      file descriptor to close
+     */
+    private SocketCleanable(Object obj, Cleaner cleaner, int fd) {
+        super(obj, cleaner);
+        this.fd = fd;
+    }
+
+    /**
+     * Close the native handle or fd.
+     */
+    @Override
+    protected void performCleanup() {
+        try {
+            cleanupClose0(fd);
+        } catch (IOException ioe) {
+            throw new UncheckedIOException("close", ioe);
+        }
+    }
+}
--- a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,8 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 
+import jdk.internal.ref.PhantomCleanable;
+
 /*
  * @author Chris Hegarty
  */
@@ -38,6 +40,8 @@
     public boolean getAppend(FileDescriptor fdo);
     public void close(FileDescriptor fdo) throws IOException;
     public void registerCleanup(FileDescriptor fdo);
+    public void registerCleanup(FileDescriptor fdo, PhantomCleanable<Object> cleanable);
+    public void unregisterCleanup(FileDescriptor fdo);
 
     // Only valid on Windows
     public void setHandle(FileDescriptor fdo, long handle);
--- a/src/java.base/unix/classes/java/io/FileDescriptor.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/unix/classes/java/io/FileDescriptor.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -91,6 +91,14 @@
                         fdo.registerCleanup();
                     }
 
+                    public void registerCleanup(FileDescriptor fdo, PhantomCleanable<Object> cleanup) {
+                        fdo.registerCleanup(cleanup);
+                    }
+
+                    public void unregisterCleanup(FileDescriptor fdo) {
+                        fdo.unregisterCleanup();
+                    }
+
                     public void setHandle(FileDescriptor fdo, long handle) {
                         throw new UnsupportedOperationException();
                     }
@@ -105,7 +113,7 @@
     /**
      * Cleanup in case FileDescriptor is not explicitly closed.
      */
-    private FDCleanup cleanup;
+    private PhantomCleanable<Object> cleanup;
 
     /**
      * Constructs an (invalid) FileDescriptor
@@ -206,16 +214,44 @@
     }
 
     /**
-     * Register a cleanup for the current raw fd.
+     * Register a cleanup for the current handle.
      * Used directly in java.io and indirectly via fdAccess.
-     * The cleanup should be registered after the fd is set in the FileDescriptor.
+     * The cleanup should be registered after the handle is set in the FileDescriptor.
      */
     @SuppressWarnings("unchecked")
-    synchronized void registerCleanup() {
+    void registerCleanup() {
+        registerCleanup(null);
+    }
+
+    /**
+     * Register a cleanup for the current handle.
+     * Used directly in java.io and indirectly via fdAccess.
+     * The cleanup should be registered after the handle is set in the FileDescriptor.
+     * @param newCleanable a PhantomCleanable to register
+     */
+    @SuppressWarnings("unchecked")
+    synchronized void registerCleanup(PhantomCleanable<Object> newCleanable) {
         if (cleanup != null) {
             cleanup.clear();
         }
-        cleanup = FDCleanup.create(this);
+        cleanup = (newCleanable == null) ? FDCleanup.create(this) : newCleanable;
+    }
+
+    /**
+     * Unregister a cleanup for the current raw fd.
+     * Used directly in java.io and indirectly via fdAccess.
+     * Normally {@link #close()} should be used except in cases where
+     * it is certain the caller will close the raw fd and the cleanup
+     * must not close the raw fd.  {@link #unregisterCleanup()} must be
+     * called before the raw fd is closed to prevent a race that makes
+     * it possible for the fd to be reallocated to another use and later
+     * the cleanup might be invoked.
+     */
+    synchronized void unregisterCleanup() {
+        if (cleanup != null) {
+            cleanup.clear();
+        }
+        cleanup = null;
     }
 
     /**
--- a/src/java.base/unix/native/libnet/SocketImpl.c	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/unix/native/libnet/SocketImpl.c	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
 #include <string.h>
 
 #include "net_util.h"
+#include "java_net_SocketCleanable.h"
 
 JNIEXPORT jboolean JNICALL
 Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
@@ -45,3 +46,15 @@
 {
     return (reuseport_available()) ? JNI_TRUE : JNI_FALSE;
 }
+
+/*
+ * Class:     java_net_SocketCleanable
+ * Method:    cleanupClose0
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_java_net_SocketCleanable_cleanupClose0(JNIEnv *env, jclass c1, jint fd)
+{
+    NET_SocketClose(fd);
+}
+
--- a/src/java.base/windows/classes/java/io/FileDescriptor.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/windows/classes/java/io/FileDescriptor.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -90,7 +90,15 @@
                     }
 
                     public void registerCleanup(FileDescriptor fdo) {
-                        fdo.registerCleanup();
+                        fdo.registerCleanup(null);
+                    }
+
+                    public void registerCleanup(FileDescriptor fdo, PhantomCleanable<Object> cleanup) {
+                        fdo.registerCleanup(cleanup);
+                    }
+
+                    public void unregisterCleanup(FileDescriptor fdo) {
+                        fdo.unregisterCleanup();
                     }
 
                     public void setHandle(FileDescriptor fdo, long handle) {
@@ -107,7 +115,7 @@
     /**
      * Cleanup in case FileDescriptor is not explicitly closed.
      */
-    private FDCleanup cleanup;
+    private PhantomCleanable<Object> cleanup;
 
     /**
      * Constructs an (invalid) FileDescriptor
@@ -217,11 +225,39 @@
      * The cleanup should be registered after the handle is set in the FileDescriptor.
      */
     @SuppressWarnings("unchecked")
-    synchronized void registerCleanup() {
+    void registerCleanup() {
+        registerCleanup(null);
+    }
+
+    /**
+     * Register a cleanup for the current handle.
+     * Used directly in java.io and indirectly via fdAccess.
+     * The cleanup should be registered after the handle is set in the FileDescriptor.
+     * @param newCleanable a PhantomCleanable to register
+     */
+    @SuppressWarnings("unchecked")
+    synchronized void registerCleanup(PhantomCleanable<Object> newCleanable) {
         if (cleanup != null) {
             cleanup.clear();
         }
-        cleanup = FDCleanup.create(this);
+        cleanup = (newCleanable == null) ? FDCleanup.create(this) : newCleanable;
+    }
+
+   /**
+     * Unregister a cleanup for the current raw fd.
+     * Used directly in java.io and indirectly via fdAccess.
+     * Normally {@link #close()} should be used except in cases where
+     * it is certain the caller will close the raw fd and the cleanup
+     * must not close the raw fd.  {@link #unregisterCleanup()} must be
+     * called before the raw fd is closed to prevent a race that makes
+     * it possible for the fd to be reallocated to another use and later
+     * the cleanup might be invoked.
+     */
+    synchronized void unregisterCleanup() {
+        if (cleanup != null) {
+            cleanup.clear();
+        }
+        cleanup = null;
     }
 
     /**
@@ -319,16 +355,21 @@
     /**
      * Cleanup for a FileDescriptor when it becomes phantom reachable.
      * Create a cleanup if handle != -1.
+     * Windows closes files using handles and sockets via the fd.
+     * Network FileDescriptors using socket fd must provide their
+     * own PhantomCleanable to {@link #registerCleanup}.
+     * This implementation only clears thehandles.
+     * <p>
      * Subclassed from {@code PhantomCleanable} so that {@code clear} can be
-     * called to disable the cleanup when the fd is closed by any means other
+     * called to disable the cleanup when the handle is closed by any means other
      * than calling {@link FileDescriptor#close}.
-     * Otherwise, it may close the handle after it has been reused.
+     * Otherwise, it may incorrectly close the handle after it has been reused.
      */
     static final class FDCleanup extends PhantomCleanable<Object> {
         private final long handle;
 
         static FDCleanup create(FileDescriptor fdo) {
-            return fdo.handle == -1
+            return fdo.handle == -1L
                     ? null
                     : new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.handle);
         }
--- a/src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java	Fri Feb 02 14:17:07 2018 -0500
@@ -86,6 +86,7 @@
         fd1 = new FileDescriptor();
         try {
             super.create();
+            SocketCleanable.register(fd1);
         } catch (SocketException e) {
             fd1 = null;
             throw e;
@@ -160,6 +161,8 @@
 
     protected void close() {
         if (fd != null || fd1 != null) {
+            SocketCleanable.unregister(fd);
+            SocketCleanable.unregister(fd1);
             datagramSocketClose();
             ResourceManager.afterUdpClose();
             fd = null;
--- a/src/java.base/windows/native/libnet/SocketImpl.c	Fri Feb 02 10:32:59 2018 -0800
+++ b/src/java.base/windows/native/libnet/SocketImpl.c	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,8 @@
  */
 
 #include <jni.h>
+#include "net_util.h"
+#include "java_net_SocketCleanable.h"
 
 JNIEXPORT jboolean JNICALL
 Java_java_net_AbstractPlainSocketImpl_isReusePortAvailable0(JNIEnv* env, jclass c1)
@@ -45,3 +47,15 @@
     // SO_REUSEPORT is not supported on Windows
     return JNI_FALSE;
 }
+
+/*
+ * Class:     java_net_SocketCleanable
+ * Method:    cleanupClose0
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_java_net_SocketCleanable_cleanupClose0(JNIEnv *env, jclass c1, jint fd)
+{
+    NET_SocketClose(fd);
+}
+
--- a/test/jdk/java/net/DatagramSocket/SendSize.java	Fri Feb 02 10:32:59 2018 -0800
+++ b/test/jdk/java/net/DatagramSocket/SendSize.java	Fri Feb 02 14:17:07 2018 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,7 @@
 /* @test
  *
  * @bug 4095393
- *
+ * @test main SendSize
  * @summary this tests a regression in 1.2, beta 2 and earlier where
  * the DatagramPackets sent the entire buffer, not the buffer length
  * of the packet.
@@ -43,8 +43,12 @@
 
     public static void main(String[] args) throws Exception {
         DatagramSocket serverSocket = new DatagramSocket();
-        new ServerThread(serverSocket).start();
-        new ClientThread(serverSocket.getLocalPort()).start();
+        Thread server = new ServerThread(serverSocket);
+        server.start();
+        Thread client = new ClientThread(serverSocket.getLocalPort());
+        client.start();
+        server.join();
+        client.join();
     }
 
     static class ServerThread extends Thread {
@@ -58,24 +62,26 @@
             try {
                 System.err.println("started server thread: " + server);
                 byte[] buf = new byte[1024];
-                DatagramPacket receivePacket = new DatagramPacket(buf,
-                                                                  buf.length);
-                server.receive(receivePacket);
-                int len = receivePacket.getLength();
-                switch(len) {
-                case packetLength:
-                    System.out.println("receive length is: " + len);
-                    break;
-                default:
-                    throw new RuntimeException(
-                        "receive length is: " + len +
-                        ", send length: " + packetLength +
-                        ", buffer length: " + buf.length);
+                for (int i = 0; i < 10; i++) {
+                    DatagramPacket receivePacket = new DatagramPacket(buf,
+                            buf.length);
+                    server.receive(receivePacket);
+                    int len = receivePacket.getLength();
+                    switch (len) {
+                        case packetLength:
+                            System.out.println("receive length is: " + len);
+                            break;
+                        default:
+                            throw new RuntimeException(
+                                    "receive length is: " + len +
+                                            ", send length: " + packetLength +
+                                            ", buffer length: " + buf.length);
+                    }
                 }
                 return;
             } catch (Exception e) {
                 e.printStackTrace();
-                throw new RuntimeException("caugth: " + e);
+                throw new RuntimeException("caught: " + e);
             } finally {
                 if (server != null) { server.close(); }
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/DatagramSocket/UnreferencedDatagramSockets.java	Fri Feb 02 14:17:07 2018 -0500
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @modules java.management java.base/java.io:+open java.base/java.net:+open
+ * @run main/othervm UnreferencedDatagramSockets
+ * @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedDatagramSockets
+ * @summary Check that unreferenced datagram sockets are closed
+ */
+
+import java.io.FileDescriptor;
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.DatagramSocketImpl;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import com.sun.management.UnixOperatingSystemMXBean;
+
+public class UnreferencedDatagramSockets {
+
+    /**
+     * The set of sockets we have to check up on.
+     */
+    final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(5);
+
+    /**
+     * Queued objects when they are unreferenced.
+     */
+    final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
+
+    // Server to echo a datagram packet
+    static class Server implements Runnable {
+
+        DatagramSocket ss;
+
+        Server() throws IOException {
+            ss = new DatagramSocket(0);
+            System.out.printf("  DatagramServer addr: %s: %d%n",
+                    this.getHost(), this.getPort());
+            pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverDatagramSocket"));
+            extractRefs(ss, "serverDatagramSocket");
+        }
+
+        InetAddress getHost() throws UnknownHostException {
+            InetAddress localhost = InetAddress.getByName("localhost"); //.getLocalHost();
+            return localhost;
+        }
+
+        int getPort() {
+            return ss.getLocalPort();
+        }
+
+        // Receive a byte and send back a byte
+        public void run() {
+            try {
+                byte[] buffer = new byte[50];
+                DatagramPacket p = new DatagramPacket(buffer, buffer.length);
+                ss.receive(p);
+                buffer[0] += 1;
+                ss.send(p);         // send back +1
+
+                // do NOT close but 'forget' the datagram socket reference
+                ss = null;
+            } catch (Exception ioe) {
+                ioe.printStackTrace();
+            }
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+
+        // Create and close a DatagramSocket to warm up the FD count for side effects.
+        try (DatagramSocket s = new DatagramSocket(0)) {
+            // no-op; close immediately
+            s.getLocalPort();   // no-op
+        }
+
+        long fdCount0 = getFdCount();
+        listProcFD();
+
+        // start a server
+        Server svr = new Server();
+        Thread thr = new Thread(svr);
+        thr.start();
+
+        DatagramSocket client = new DatagramSocket(0);
+        client.connect(svr.getHost(), svr.getPort());
+        pendingSockets.add(new NamedWeak(client, pendingQueue, "clientDatagramSocket"));
+        extractRefs(client, "clientDatagramSocket");
+
+        byte[] msg = new byte[1];
+        msg[0] = 1;
+        DatagramPacket p = new DatagramPacket(msg, msg.length, svr.getHost(), svr.getPort());
+        client.send(p);
+
+        p = new DatagramPacket(msg, msg.length);
+        client.receive(p);
+
+        System.out.printf("echo received from: %s%n", p.getSocketAddress());
+        if (msg[0] != 2) {
+            throw new AssertionError("incorrect data received: expected: 2, actual: " + msg[0]);
+        }
+
+        // Do NOT close the DatagramSocket; forget it
+
+        Object ref;
+        int loops = 20;
+        while (!pendingSockets.isEmpty() && loops-- > 0) {
+            ref = pendingQueue.remove(1000L);
+            if (ref != null) {
+                pendingSockets.remove(ref);
+                System.out.printf("  ref freed: %s, remaining: %d%n", ref, pendingSockets.size());
+            } else {
+                client = null;
+                p = null;
+                msg = null;
+                System.gc();
+            }
+        }
+
+        thr.join();
+
+        // List the open file descriptors
+        long fdCount = getFdCount();
+        System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
+        listProcFD();
+
+        if (loops == 0) {
+            throw new AssertionError("Not all references reclaimed");
+        }
+    }
+
+    // Get the count of open file descriptors, or -1 if not available
+    private static long getFdCount() {
+        OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
+        return (mxBean instanceof UnixOperatingSystemMXBean)
+                ? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
+                : -1L;
+    }
+
+    // Reflect to find references in the datagram implementation that will be gc'd
+    private static void extractRefs(DatagramSocket s, String name) {
+        try {
+
+            Field socketImplField = DatagramSocket.class.getDeclaredField("impl");
+            socketImplField.setAccessible(true);
+            Object socketImpl = socketImplField.get(s);
+
+            Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
+            fileDescriptorField.setAccessible(true);
+            FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
+            extractRefs(fileDescriptor, name);
+
+            Class<?> socketImplClass = socketImpl.getClass();
+            System.out.printf("socketImplClass: %s%n", socketImplClass);
+            if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
+                Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
+                fileDescriptor1Field.setAccessible(true);
+                FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
+                extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+
+            } else {
+                System.out.printf("socketImpl class name not matched: %s != %s%n",
+                        socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
+            }
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        }
+    }
+
+    private static void extractRefs(FileDescriptor fileDescriptor, String name) {
+        Object cleanup = null;
+        int rawfd = -1;
+        try {
+            if (fileDescriptor != null) {
+                Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
+                fd1Field.setAccessible(true);
+                rawfd = fd1Field.getInt(fileDescriptor);
+
+                Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
+                cleanupfdField.setAccessible(true);
+                cleanup = cleanupfdField.get(fileDescriptor);
+                pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
+                        name + "::fileDescriptor: " + rawfd));
+                pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
+
+            }
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        } finally {
+            System.out.print(String.format("  %s:: fd: %s, fd: %d, cleanup: %s%n",
+                    name, fileDescriptor, rawfd, cleanup));
+        }
+    }
+
+    /**
+     * Method to list the open file descriptors (if supported by the 'lsof' command).
+     */
+    static void listProcFD() {
+        List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
+        Optional<Path> lsof = lsofDirs.stream()
+                .map(s -> Paths.get(s, "lsof"))
+                .filter(f -> Files.isExecutable(f))
+                .findFirst();
+        lsof.ifPresent(exe -> {
+            try {
+                System.out.printf("Open File Descriptors:%n");
+                long pid = ProcessHandle.current().pid();
+                ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
+                pb.inheritIO();
+                Process p = pb.start();
+                p.waitFor(10, TimeUnit.SECONDS);
+            } catch (IOException | InterruptedException ie) {
+                ie.printStackTrace();
+            }
+        });
+    }
+
+    // Simple class to identify which refs have been queued
+    static class NamedWeak extends WeakReference<Object> {
+        private final String name;
+
+        NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
+            super(o, queue);
+            this.name = name;
+        }
+
+        public String toString() {
+            return name + "; " + super.toString();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/MulticastSocket/UnreferencedMulticastSockets.java	Fri Feb 02 14:17:07 2018 -0500
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @modules java.management java.base/java.io:+open java.base/java.net:+open
+ * @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedMulticastSockets
+ * @run main/othervm UnreferencedMulticastSockets
+ * @summary Check that unreferenced multicast sockets are closed
+ */
+
+import java.io.FileDescriptor;
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.DatagramSocketImpl;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import com.sun.management.UnixOperatingSystemMXBean;
+
+public class UnreferencedMulticastSockets {
+
+    /**
+     * The set of sockets we have to check up on.
+     */
+    final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(5);
+
+    /**
+     * Queued objects when they are unreferenced.
+     */
+    final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
+
+    // Server to echo a datagram packet
+    static class Server implements Runnable {
+
+        MulticastSocket ss;
+
+        Server() throws IOException {
+            ss = new MulticastSocket(0);
+            System.out.printf("  DatagramServer addr: %s: %d%n",
+                    this.getHost(), this.getPort());
+            pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverMulticastSocket"));
+            extractRefs(ss, "serverMulticastSocket");
+        }
+
+        InetAddress getHost() throws UnknownHostException {
+            InetAddress localhost = InetAddress.getByName("localhost"); //.getLocalHost();
+            return localhost;
+        }
+
+        int getPort() {
+            return ss.getLocalPort();
+        }
+
+        // Receive a byte and send back a byte
+        public void run() {
+            try {
+                byte[] buffer = new byte[50];
+                DatagramPacket p = new DatagramPacket(buffer, buffer.length);
+                ss.receive(p);
+                buffer[0] += 1;
+                ss.send(p);         // send back +1
+
+                // do NOT close but 'forget' the socket reference
+                ss = null;
+            } catch (Exception ioe) {
+                ioe.printStackTrace();
+            }
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+
+        // Create and close a MulticastSocket to warm up the FD count for side effects.
+        try (MulticastSocket s = new MulticastSocket(0)) {
+            // no-op; close immediately
+            s.getLocalPort();   // no-op
+        }
+
+        long fdCount0 = getFdCount();
+        listProcFD();
+
+        // start a server
+        Server svr = new Server();
+        Thread thr = new Thread(svr);
+        thr.start();
+
+        MulticastSocket client = new MulticastSocket(0);
+        client.connect(svr.getHost(), svr.getPort());
+        pendingSockets.add(new NamedWeak(client, pendingQueue, "clientMulticastSocket"));
+        extractRefs(client, "clientMulticastSocket");
+
+        byte[] msg = new byte[1];
+        msg[0] = 1;
+        DatagramPacket p = new DatagramPacket(msg, msg.length, svr.getHost(), svr.getPort());
+        client.send(p);
+
+        p = new DatagramPacket(msg, msg.length);
+        client.receive(p);
+
+        System.out.printf("echo received from: %s%n", p.getSocketAddress());
+        if (msg[0] != 2) {
+            throw new AssertionError("incorrect data received: expected: 2, actual: " + msg[0]);
+        }
+
+        // Do NOT close the MulticastSocket; forget it
+
+        Object ref;
+        int loops = 20;
+        while (!pendingSockets.isEmpty() && loops-- > 0) {
+            ref = pendingQueue.remove(1000L);
+            if (ref != null) {
+                pendingSockets.remove(ref);
+                System.out.printf("  ref freed: %s, remaining: %d%n", ref, pendingSockets.size());
+            } else {
+                client = null;
+                p = null;
+                msg = null;
+                System.gc();
+            }
+        }
+
+        thr.join();
+
+        // List the open file descriptors
+        long fdCount = getFdCount();
+        System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
+        listProcFD();
+
+        if (loops == 0) {
+            throw new AssertionError("Not all references reclaimed");
+        }
+    }
+
+    // Get the count of open file descriptors, or -1 if not available
+    private static long getFdCount() {
+        OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
+        return (mxBean instanceof UnixOperatingSystemMXBean)
+                ? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
+                : -1L;
+    }
+
+    // Reflect to find references in the socket implementation that will be gc'd
+    private static void extractRefs(MulticastSocket s, String name) {
+        try {
+
+            Field socketImplField = DatagramSocket.class.getDeclaredField("impl");
+            socketImplField.setAccessible(true);
+            Object socketImpl = socketImplField.get(s);
+
+            Field fileDescriptorField = DatagramSocketImpl.class.getDeclaredField("fd");
+            fileDescriptorField.setAccessible(true);
+            FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
+            extractRefs(fileDescriptor, name);
+
+            Class<?> socketImplClass = socketImpl.getClass();
+            System.out.printf("socketImplClass: %s%n", socketImplClass);
+            if (socketImplClass.getName().equals("java.net.TwoStacksPlainDatagramSocketImpl")) {
+                Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
+                fileDescriptor1Field.setAccessible(true);
+                FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
+                extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+
+            } else {
+                System.out.printf("socketImpl class name not matched: %s != %s%n",
+                        socketImplClass.getName(), "java.net.TwoStacksPlainDatagramSocketImpl");
+            }
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        }
+    }
+
+    private static void extractRefs(FileDescriptor fileDescriptor, String name) {
+        Object cleanup = null;
+        int rawfd = -1;
+        try {
+            if (fileDescriptor != null) {
+                Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
+                fd1Field.setAccessible(true);
+                rawfd = fd1Field.getInt(fileDescriptor);
+
+                Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
+                cleanupfdField.setAccessible(true);
+                cleanup = cleanupfdField.get(fileDescriptor);
+                pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
+                        name + "::fileDescriptor: " + rawfd));
+                pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
+
+            }
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        } finally {
+            System.out.print(String.format("  %s:: fd: %s, fd: %d, cleanup: %s%n",
+                    name, fileDescriptor, rawfd, cleanup));
+        }
+    }
+
+    /**
+     * Method to list the open file descriptors (if supported by the 'lsof' command).
+     */
+    static void listProcFD() {
+        List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
+        Optional<Path> lsof = lsofDirs.stream()
+                .map(s -> Paths.get(s, "lsof"))
+                .filter(f -> Files.isExecutable(f))
+                .findFirst();
+        lsof.ifPresent(exe -> {
+            try {
+                System.out.printf("Open File Descriptors:%n");
+                long pid = ProcessHandle.current().pid();
+                ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
+                pb.inheritIO();
+                Process p = pb.start();
+                p.waitFor(10, TimeUnit.SECONDS);
+            } catch (IOException | InterruptedException ie) {
+                ie.printStackTrace();
+            }
+        });
+    }
+
+
+    // Simple class to identify which refs have been queued
+    static class NamedWeak extends WeakReference<Object> {
+        private final String name;
+
+        NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
+            super(o, queue);
+            this.name = name;
+        }
+
+        public String toString() {
+            return name + "; " + super.toString();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/ServerSocket/UnreferencedSockets.java	Fri Feb 02 14:17:07 2018 -0500
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @modules java.management java.base/java.io:+open java.base/java.net:+open
+ * @run main/othervm UnreferencedSockets
+ * @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedSockets
+ * @summary Check that unreferenced sockets are closed
+ */
+
+import java.io.FileDescriptor;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketImpl;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+import com.sun.management.UnixOperatingSystemMXBean;
+
+public class UnreferencedSockets {
+
+    /**
+     * The set of sockets we have to check up on.
+     */
+    final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(100);
+
+    /**
+     * Queued sockets when they are unreferenced.
+     */
+    final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();
+
+    // Server to echo a stream
+    static class Server implements Runnable {
+
+        ServerSocket ss;
+
+        Server() throws IOException {
+            ss = new ServerSocket(0);
+            pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverSocket"));
+            extractRefs(ss, "serverSocket");
+        }
+
+        public int localPort() {
+            return ss.getLocalPort();
+        }
+
+
+        public void run() {
+            try {
+                Socket s = ss.accept();
+                pendingSockets.add(new NamedWeak(s, pendingQueue, "acceptedSocket"));
+                extractRefs(s, "acceptedSocket");
+
+                InputStream in = s.getInputStream();
+                int b = in.read();
+                OutputStream out = s.getOutputStream();
+                out.write(b);
+                // do NOT close but 'forget' the socket reference
+                out = null;
+                in = null;
+                s = null;
+            } catch (Exception ioe) {
+                ioe.printStackTrace();
+            } finally {
+                try {
+                    ss.close();
+                    ss = null;
+                } catch (IOException x) {
+                    x.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public static void main(String args[]) throws Exception {
+
+        // Create and close a ServerSocket to warm up the FD count for side effects.
+        try (ServerSocket s = new ServerSocket(0)) {
+            // no-op; close immediately
+            s.getLocalPort();   // no-op
+        }
+
+        long fdCount0 = getFdCount();
+        listProcFD();
+
+        // start a server
+        Server svr = new Server();
+        Thread thr = new Thread(svr);
+        thr.start();
+
+        Socket s = new Socket("localhost", svr.localPort());
+        pendingSockets.add(new NamedWeak(s, pendingQueue, "clientSocket"));
+        extractRefs(s, "clientSocket");
+
+        OutputStream out = s.getOutputStream();
+        out.write('x');
+        out.flush();
+        InputStream in = s.getInputStream();
+        int b = in.read();  // wait for it back
+        System.out.printf("  data sent and received%n");
+        // Do NOT close the Socket; forget it
+
+        Object ref;
+        int loops = 20;
+        while (!pendingSockets.isEmpty() && loops-- > 0) {
+            ref = pendingQueue.remove(1000L);
+            if (ref != null) {
+                pendingSockets.remove(ref);
+                System.out.printf("  ref queued: %s, remaining: %d%n", ref, pendingSockets.size());
+            } else {
+                s = null;
+                out = null;
+                in = null;
+                System.gc();
+            }
+        }
+
+        thr.join();
+
+        // List the open file descriptors
+        long fdCount = getFdCount();
+        System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);
+        listProcFD();
+
+        if (loops == 0) {
+            throw new AssertionError("Not all references reclaimed");
+        }
+    }
+
+    // Get the count of open file descriptors, or -1 if not available
+    private static long getFdCount() {
+        OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();
+        return (mxBean instanceof UnixOperatingSystemMXBean)
+                ? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()
+                : -1L;
+    }
+
+    // Reflect to find references in the socket implementation that will be gc'd
+    private static void extractRefs(Socket s, String name) {
+        try {
+
+            Field socketImplField = Socket.class.getDeclaredField("impl");
+            socketImplField.setAccessible(true);
+            Object socketImpl = socketImplField.get(s);
+
+            Field fileDescriptorField = SocketImpl.class.getDeclaredField("fd");
+            fileDescriptorField.setAccessible(true);
+            FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
+            extractRefs(fileDescriptor, name);
+
+            Class<?> socketImplClass = socketImpl.getClass();
+            System.out.printf("socketImplClass: %s%n", socketImplClass);
+            if (socketImplClass.getClass().getName().equals("java.net.TwoStacksPlainSocketImpl")) {
+                Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");
+                fileDescriptor1Field.setAccessible(true);
+                FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);
+                extractRefs(fileDescriptor1, name + "::twoStacksFd1");
+
+            }
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        }
+    }
+
+    private static void extractRefs(FileDescriptor fileDescriptor, String name) {
+        Object cleanup = null;
+        int rawfd = -1;
+        try {
+            if (fileDescriptor != null) {
+                Field fd1Field = FileDescriptor.class.getDeclaredField("fd");
+                fd1Field.setAccessible(true);
+                rawfd = fd1Field.getInt(fileDescriptor);
+
+                Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");
+                cleanupfdField.setAccessible(true);
+                cleanup = cleanupfdField.get(fileDescriptor);
+                pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
+                        name + "::fileDescriptor: " + rawfd));
+                pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
+
+            }
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        } finally {
+            System.out.print(String.format("  fd: %s, fd: %d, cleanup: %s%n",
+                    fileDescriptor, rawfd, cleanup));
+        }
+    }
+
+    private static void extractRefs(ServerSocket s, String name) {
+        try {
+
+            Field socketImplField = ServerSocket.class.getDeclaredField("impl");
+            socketImplField.setAccessible(true);
+            Object socketImpl = socketImplField.get(s);
+
+            Field fileDescriptorField = SocketImpl.class.getDeclaredField("fd");
+            fileDescriptorField.setAccessible(true);
+            FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);
+
+            Field fdField = FileDescriptor.class.getDeclaredField("fd");
+            fdField.setAccessible(true);
+            int rawfd = fdField.getInt(fileDescriptor);
+
+            Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");
+            cleanupField.setAccessible(true);
+            Object cleanup = cleanupField.get(fileDescriptor);
+
+            System.out.print(String.format("  fd: %s, fd: %d, cleanup: %s, socket: %s%n",
+                    fileDescriptor, rawfd, cleanup, s));
+
+            pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,
+                    name + "::fileDescriptor: " + rawfd));
+            pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));
+
+        } catch (NoSuchFieldException | IllegalAccessException ex) {
+            ex.printStackTrace();
+            throw new AssertionError("missing field", ex);
+        }
+    }
+
+    /**
+     * Method to list the open file descriptors (if supported by the 'lsof' command).
+     */
+    static void listProcFD() {
+        List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");
+        Optional<Path> lsof = lsofDirs.stream()
+                .map(s -> Paths.get(s, "lsof"))
+                .filter(f -> Files.isExecutable(f))
+                .findFirst();
+        lsof.ifPresent(exe -> {
+            try {
+                System.out.printf("Open File Descriptors:%n");
+                long pid = ProcessHandle.current().pid();
+                ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));
+                pb.inheritIO();
+                Process p = pb.start();
+                p.waitFor(10, TimeUnit.SECONDS);
+            } catch (IOException | InterruptedException ie) {
+                ie.printStackTrace();
+            }
+        });
+    }
+
+    // Simple class to identify which refs have been queued
+    static class NamedWeak extends WeakReference<Object> {
+        private final String name;
+
+        NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {
+            super(o, queue);
+            this.name = name;
+        }
+
+        public String toString() {
+            return name + "; " + super.toString();
+        }
+    }
+}