JDK-8228568 : resource leak caused by the setting or getting a socket option on SocketChannel
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 11,12
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86_64
  • Submitted: 2019-07-17
  • Updated: 2020-01-16
  • Resolved: 2019-09-23
Related Reports
Duplicate :  
Description
A DESCRIPTION OF THE PROBLEM :
If one sets jdk.net.ExtendedSocketOptions on a socket channel there seems to be a resource leak for socket file handles. This is reproduceable and can be obtained by the lsof tool in Linux Systems.


---------- BEGIN SOURCE ----------
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Scanner;

import jdk.net.ExtendedSocketOptions;

/**
 * Test-code to reproduce resource leak caused by the usage of ExendedSocketOptions
 *
 * Linux Distribution:
 *                      NAME="openSUSE Leap"
 *                      VERSION="15.1 "
 *                      ID="opensuse-leap"
 *                      ID_LIKE="suse opensuse"
 *                      VERSION_ID="15.1"
 *                      PRETTY_NAME="openSUSE Leap 15.1"
 *                      ANSI_COLOR="0;32"
 *                      CPE_NAME="cpe:/o:opensuse:leap:15.1"
 *                      BUG_REPORT_URL="https://bugs.opensuse.org"
 *                      HOME_URL="https://www.opensuse.org/"
 *                      
 * Java-Version:
 *                      openjdk 12.0.2 2019-07-16
 *                      OpenJDK Runtime Environment (build 12.0.2+10)
 *                      OpenJDK 64-Bit Server VM (build 12.0.2+10, mixed mode, sharing)
 *
 * Compile with:
 *                      javac SocketTest.java
 * Run with:
 *                      java SocketTest [port number]
 * 
 * to detect resource leak, use unix command lsof -P -p [pid of process]
 * this command yields a large number of entries of the form
 * java    pid user  104u  sock                0,9       0t0  5791955 protocol: TCPv6
 *
 */
public class SocketTest {
    private static final int TESTNUM = 100;

    public static void main(String[] args) {
        if (args.length < 2) {
            printUsage();
        } else {
            try {
                final int port = Integer.parseInt(args[0]);
                final int num = Integer.parseInt(args[1]);
                final boolean useExtendedSocketOptions = num > 0;
                new SocketTest().doTest(port, useExtendedSocketOptions);
            } catch (NumberFormatException e) {
                printUsage();
            }
        }
        
    }


    public static void printUsage() {
        System.out.println("usage: java SocketTest [port] [0 (=without extended socket options)/1 (=with extended socket options)]");
    }


    private void doTest(final int port, boolean useExtendedSocketOptions) {
        try(ServerSocketChannel ssc = ServerSocketChannel.open()) {
            ssc.configureBlocking(true);
            ssc.socket().bind(new InetSocketAddress(port));

            final Thread acceptorThread = new Thread("acceptor") {
                int ctr = 0;
                @Override
                public void run() {
                    while(ctr < TESTNUM) {
                        try(SocketChannel sc = ssc.accept()) {
                            System.out.println("accepted counter=" + ++ctr);
                            // resource leak is caused by the next line
                            if (useExtendedSocketOptions) {
                                sc.socket().setOption(ExtendedSocketOptions.TCP_KEEPIDLE, Integer.valueOf(30));
                            }
                        } catch (UnsupportedOperationException e) {
                            System.out.println(e.getMessage());
                        } catch (IOException e) {
                            throw new RuntimeException(e.getMessage(), e);
                        }
                    }
                }
            };  


            final Thread clientThread = new Thread("client") {
                @Override
                public void run() {
                    for (int i = 1; i <= TESTNUM; i++) {
                        try (Socket s = new Socket("localhost", port)) {
                            System.out.println("open counter=" + i);
                        } catch (IOException e) {
                            throw new RuntimeException(e.getMessage(), e);
                        }
                    }
                }
            };

            acceptorThread.start();
            clientThread.start();

            clientThread.join();
            System.out.println("clientThread joined");
            acceptorThread.join();
            System.out.println("acceptorThread joined");
            
            try (Scanner keyIn = new Scanner(System.in)) {
                System.out.print("please execute lsof -P -p [pid of this process], then press enter key to continue");
                keyIn.nextLine();
            }

        } catch (IOException | InterruptedException e) {
            throw new RuntimeException(e.getMessage(), e);
        }



    }
}

---------- END SOURCE ----------

FREQUENCY : always



Comments
As a side-effect of the fix for JDK-8222774, this issue has been resolved.
23-09-2019

The issue is with setting options through the socket adapter. It is not limited to extended options, but affects all options. One such example (from the description section): // sc.socket().setOption(ExtendedSocketOptions.TCP_KEEPIDLE, Integer.valueOf(30)); Prior to JDK 13 ( see JDK-8222774), the socket adapter set/getOption methods are inherited from java.net.Socket, where they will result in the creation of a legacy Socket impl ( and backing socket ). In fact, the option value will be set on the legacy socket impl, and not the socket adapter.
16-09-2019

Closing as Cannot reproduce in latest JDK version.
24-07-2019

This is not reproducible in JDK 13. Requesting submitter to verify in JDK 13-ea version.
24-07-2019