Path: blob/master/test/jdk/java/net/ServerSocket/UnreferencedSockets.java
41152 views
/*1* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @library /test/lib26* @modules java.management java.base/java.io:+open java.base/java.net:+open27* @run main/othervm UnreferencedSockets28* @run main/othervm -Djava.net.preferIPv4Stack=true UnreferencedSockets29* @run main/othervm -Djdk.net.usePlainSocketImpl UnreferencedSockets30* @summary Check that unreferenced sockets are closed31*/3233import java.io.FileDescriptor;34import java.io.InputStream;35import java.io.OutputStream;36import java.lang.management.ManagementFactory;37import java.lang.management.OperatingSystemMXBean;38import java.lang.ref.ReferenceQueue;39import java.lang.ref.WeakReference;40import java.lang.reflect.Field;41import java.io.IOException;42import java.net.InetAddress;43import java.net.ServerSocket;44import java.net.Socket;45import java.net.SocketImpl;46import java.nio.file.Files;47import java.nio.file.Path;48import java.nio.file.Paths;49import java.util.ArrayDeque;50import java.util.List;51import java.util.Optional;52import java.util.concurrent.TimeUnit;5354import com.sun.management.UnixOperatingSystemMXBean;5556import jdk.test.lib.net.IPSupport;5758public class UnreferencedSockets {5960/**61* The set of sockets we have to check up on.62*/63final static ArrayDeque<NamedWeak> pendingSockets = new ArrayDeque<>(100);6465/**66* Queued sockets when they are unreferenced.67*/68final static ReferenceQueue<Object> pendingQueue = new ReferenceQueue<>();6970// Server to echo a stream71static class Server implements Runnable {7273ServerSocket ss;7475Server(InetAddress address) throws IOException {76ss = new ServerSocket(0, 0, address);77pendingSockets.add(new NamedWeak(ss, pendingQueue, "serverSocket"));78extractRefs(ss, "serverSocket");79}8081public int localPort() {82return ss.getLocalPort();83}8485public void run() {86try {87Socket s = ss.accept();88pendingSockets.add(new NamedWeak(s, pendingQueue, "acceptedSocket"));89extractRefs(s, "acceptedSocket");9091InputStream in = s.getInputStream();92int b = in.read();93OutputStream out = s.getOutputStream();94out.write(b);95// do NOT close but 'forget' the socket reference96out = null;97in = null;98s = null;99} catch (Exception ioe) {100ioe.printStackTrace();101} finally {102try {103ss.close();104ss = null;105} catch (IOException x) {106x.printStackTrace();107}108}109}110}111112public static void main(String args[]) throws Exception {113IPSupport.throwSkippedExceptionIfNonOperational();114InetAddress lba = InetAddress.getLoopbackAddress();115// Create and close a ServerSocket to warm up the FD count for side effects.116try (ServerSocket s = new ServerSocket(0, 0, lba)) {117// no-op; close immediately118s.getLocalPort(); // no-op119}120121long fdCount0 = getFdCount();122listProcFD();123124// start a server125Server svr = new Server(lba);126Thread thr = new Thread(svr);127thr.start();128129Socket s = new Socket(lba, svr.localPort());130pendingSockets.add(new NamedWeak(s, pendingQueue, "clientSocket"));131extractRefs(s, "clientSocket");132133OutputStream out = s.getOutputStream();134out.write('x');135out.flush();136InputStream in = s.getInputStream();137int b = in.read(); // wait for it back138System.out.printf(" data sent and received%n");139// Do NOT close the Socket; forget it140141Object ref;142int loops = 20;143while (!pendingSockets.isEmpty() && loops-- > 0) {144ref = pendingQueue.remove(1000L);145if (ref != null) {146pendingSockets.remove(ref);147System.out.printf(" ref queued: %s, remaining: %d%n", ref, pendingSockets.size());148} else {149s = null;150out = null;151in = null;152System.gc();153}154}155156thr.join();157158// List the open file descriptors159long fdCount = getFdCount();160System.out.printf("Initial fdCount: %d, final fdCount: %d%n", fdCount0, fdCount);161listProcFD();162163if (loops == 0) {164throw new AssertionError("Not all references reclaimed");165}166}167168// Get the count of open file descriptors, or -1 if not available169private static long getFdCount() {170OperatingSystemMXBean mxBean = ManagementFactory.getOperatingSystemMXBean();171return (mxBean instanceof UnixOperatingSystemMXBean)172? ((UnixOperatingSystemMXBean) mxBean).getOpenFileDescriptorCount()173: -1L;174}175176// Reflect to find references in the socket implementation that will be gc'd177private static void extractRefs(Socket s, String name) {178try {179180Field socketImplField = Socket.class.getDeclaredField("impl");181socketImplField.setAccessible(true);182Object socketImpl = socketImplField.get(s);183184Field fileDescriptorField = SocketImpl.class.getDeclaredField("fd");185fileDescriptorField.setAccessible(true);186FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);187extractRefs(fileDescriptor, name);188189Class<?> socketImplClass = socketImpl.getClass();190System.out.printf("socketImplClass: %s%n", socketImplClass);191if (socketImplClass.getClass().getName().equals("java.net.TwoStacksPlainSocketImpl")) {192Field fileDescriptor1Field = socketImplClass.getDeclaredField("fd1");193fileDescriptor1Field.setAccessible(true);194FileDescriptor fileDescriptor1 = (FileDescriptor) fileDescriptor1Field.get(socketImpl);195extractRefs(fileDescriptor1, name + "::twoStacksFd1");196197}198} catch (NoSuchFieldException | IllegalAccessException ex) {199ex.printStackTrace();200throw new AssertionError("missing field", ex);201}202}203204private static void extractRefs(FileDescriptor fileDescriptor, String name) {205Object cleanup = null;206int rawfd = -1;207try {208if (fileDescriptor != null) {209Field fd1Field = FileDescriptor.class.getDeclaredField("fd");210fd1Field.setAccessible(true);211rawfd = fd1Field.getInt(fileDescriptor);212213Field cleanupfdField = FileDescriptor.class.getDeclaredField("cleanup");214cleanupfdField.setAccessible(true);215cleanup = cleanupfdField.get(fileDescriptor);216pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,217name + "::fileDescriptor: " + rawfd));218pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));219220}221} catch (NoSuchFieldException | IllegalAccessException ex) {222ex.printStackTrace();223throw new AssertionError("missing field", ex);224} finally {225System.out.print(String.format(" fd: %s, fd: %d, cleanup: %s%n",226fileDescriptor, rawfd, cleanup));227}228}229230private static void extractRefs(ServerSocket s, String name) {231try {232233Field socketImplField = ServerSocket.class.getDeclaredField("impl");234socketImplField.setAccessible(true);235Object socketImpl = socketImplField.get(s);236237Field fileDescriptorField = SocketImpl.class.getDeclaredField("fd");238fileDescriptorField.setAccessible(true);239FileDescriptor fileDescriptor = (FileDescriptor) fileDescriptorField.get(socketImpl);240241Field fdField = FileDescriptor.class.getDeclaredField("fd");242fdField.setAccessible(true);243int rawfd = fdField.getInt(fileDescriptor);244245Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");246cleanupField.setAccessible(true);247Object cleanup = cleanupField.get(fileDescriptor);248249System.out.print(String.format(" fd: %s, fd: %d, cleanup: %s, socket: %s%n",250fileDescriptor, rawfd, cleanup, s));251252pendingSockets.add(new NamedWeak(fileDescriptor, pendingQueue,253name + "::fileDescriptor: " + rawfd));254pendingSockets.add(new NamedWeak(cleanup, pendingQueue, name + "::fdCleanup: " + rawfd));255256} catch (NoSuchFieldException | IllegalAccessException ex) {257ex.printStackTrace();258throw new AssertionError("missing field", ex);259}260}261262/**263* Method to list the open file descriptors (if supported by the 'lsof' command).264*/265static void listProcFD() {266List<String> lsofDirs = List.of("/usr/bin", "/usr/sbin");267Optional<Path> lsof = lsofDirs.stream()268.map(s -> Paths.get(s, "lsof"))269.filter(f -> Files.isExecutable(f))270.findFirst();271lsof.ifPresent(exe -> {272try {273System.out.printf("Open File Descriptors:%n");274long pid = ProcessHandle.current().pid();275ProcessBuilder pb = new ProcessBuilder(exe.toString(), "-p", Integer.toString((int) pid));276pb.inheritIO();277Process p = pb.start();278p.waitFor(10, TimeUnit.SECONDS);279} catch (IOException | InterruptedException ie) {280ie.printStackTrace();281}282});283}284285// Simple class to identify which refs have been queued286static class NamedWeak extends WeakReference<Object> {287private final String name;288289NamedWeak(Object o, ReferenceQueue<Object> queue, String name) {290super(o, queue);291this.name = name;292}293294public String toString() {295return name + "; " + super.toString();296}297}298}299300301