-
Bug
-
Resolution: Fixed
-
P3
-
1.4.2, 5.0
-
b59
-
x86
-
linux
FULL PRODUCT VERSION :
java version "1.5.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_01-b08)
Java HotSpot(TM) Server VM (build 1.5.0_01-b08, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Demonstrated on Linux (SLES 9) and Windows 2000, but not OS specific.
A DESCRIPTION OF THE PROBLEM :
Calling the socket() method on a DatagramChannel results in the creation of an unused PlainDatagramSocketImpl object (documented in the comment with the constructor for DatagramSocketAdaptor). As a result of this a file descriptor that is never used is opened. The open happens when DatagramSocket.createImpl() calls impl.create() at line 317. Normally DatagramSocket.close() would close this file descriptor, however DatagramSocketAdaptor.close() does not call super.close() so this file descriptor is never cleaned up. There are several solutions possible. The simplest is to change DatagramSocketAdaptor.close() to call super.close(). This will clean up the dangling file descriptor, however it is never used in the first place, so it really should never be created. There are two ways to make sure it is never created. The code in DatagramSocketAdaptor could be moved to a class that extends DatagramSocketImpl and a new instance of this class could be passed to the DatagramSocket constructor that takes an implementation. This is probably the most correct solution. Another solution that is easier to implement is to change the call to super in the DatagramSocketAdaptor constructor so that it passes an empty implementation that does nothing. To solve the problem for myself I made this change and use the -Xbootclasspath option to load my version of DatagramSocketAdaptor. My empty implementation looks like this:
class NullDatagramSocketImpl extends DatagramSocketImpl {
protected void create() throws SocketException {}
protected void bind(int lport, InetAddress laddr) throws SocketException {}
protected void send(DatagramPacket p) throws IOException {}
protected int peek(InetAddress i) throws IOException {
return 0;
}
protected int peekData(DatagramPacket p) throws IOException {
return 0;
}
protected void receive(DatagramPacket p) throws IOException {}
protected void setTTL(byte ttl) throws IOException {}
protected byte getTTL() throws IOException {
return 0;
}
protected void setTimeToLive(int ttl) throws IOException {}
protected int getTimeToLive() throws IOException {
return 0;
}
protected void join(InetAddress inetaddr) throws IOException {}
protected void leave(InetAddress inetaddr) throws IOException {}
protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException {}
protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException {}
protected void close() {}
public Object getOption(int optID) throws SocketException {
return null;
}
public void setOption(int optID, Object value) throws SocketException {}
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This bug is easily seen with the simple test program below and either a profiler that shows open file handles or some Linux commands to do the same thing. I used lsof on Linux and OptimizeIt on Windows to see the problem and to see that my fix worked.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Looping on opening a DatagramChannel, getting the socket, and closing all resources should result in a file descriptor count that does not increase.
ACTUAL -
The file descriptor count increases with each iteration of the loop. If the handle limit is high enough and garbage collection happens soon enough then eventually the handle count will jump down as the stranded PlainDatagramSocketImpl objects are finalized. If not, then eventually new opens will fail with an error stating that there are too many open files.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.SocketException: Too many open files
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.net.DatagramSocket;
import java.nio.channels.*;
import java.io.IOException;
public class FDLeak {
public static void main(String[] args) throws IOException {
DatagramSocket so = null;
DatagramChannel ch = null;
for(int i = 0; i < 5; i++) {
System.out.println("Opening socket in 5 seconds");
try {
Thread.sleep(5000);
} catch(InterruptedException e) {}
ch = DatagramChannel.open();
try {
so = ch.socket();
} finally {
System.out.println("Closing socket in 5 seconds");
try {
Thread.sleep(5000);
} catch(InterruptedException e) {}
if(so != null) {
so.close();
}
try {
ch.close();
} catch(IOException ex) {}
so = null;
ch = null;
}
}
System.out.println("Exiting in 5 seconds");
try {
Thread.sleep(5000);
} catch(InterruptedException e) {}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The only way around this problem is to replace sun.nio.ch.DatagramSocketAdaptor with your own implementation using -Xbootclasspath.
###@###.### 2005-03-28 06:33:56 GMT
java version "1.5.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_01-b08)
Java HotSpot(TM) Server VM (build 1.5.0_01-b08, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Demonstrated on Linux (SLES 9) and Windows 2000, but not OS specific.
A DESCRIPTION OF THE PROBLEM :
Calling the socket() method on a DatagramChannel results in the creation of an unused PlainDatagramSocketImpl object (documented in the comment with the constructor for DatagramSocketAdaptor). As a result of this a file descriptor that is never used is opened. The open happens when DatagramSocket.createImpl() calls impl.create() at line 317. Normally DatagramSocket.close() would close this file descriptor, however DatagramSocketAdaptor.close() does not call super.close() so this file descriptor is never cleaned up. There are several solutions possible. The simplest is to change DatagramSocketAdaptor.close() to call super.close(). This will clean up the dangling file descriptor, however it is never used in the first place, so it really should never be created. There are two ways to make sure it is never created. The code in DatagramSocketAdaptor could be moved to a class that extends DatagramSocketImpl and a new instance of this class could be passed to the DatagramSocket constructor that takes an implementation. This is probably the most correct solution. Another solution that is easier to implement is to change the call to super in the DatagramSocketAdaptor constructor so that it passes an empty implementation that does nothing. To solve the problem for myself I made this change and use the -Xbootclasspath option to load my version of DatagramSocketAdaptor. My empty implementation looks like this:
class NullDatagramSocketImpl extends DatagramSocketImpl {
protected void create() throws SocketException {}
protected void bind(int lport, InetAddress laddr) throws SocketException {}
protected void send(DatagramPacket p) throws IOException {}
protected int peek(InetAddress i) throws IOException {
return 0;
}
protected int peekData(DatagramPacket p) throws IOException {
return 0;
}
protected void receive(DatagramPacket p) throws IOException {}
protected void setTTL(byte ttl) throws IOException {}
protected byte getTTL() throws IOException {
return 0;
}
protected void setTimeToLive(int ttl) throws IOException {}
protected int getTimeToLive() throws IOException {
return 0;
}
protected void join(InetAddress inetaddr) throws IOException {}
protected void leave(InetAddress inetaddr) throws IOException {}
protected void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException {}
protected void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException {}
protected void close() {}
public Object getOption(int optID) throws SocketException {
return null;
}
public void setOption(int optID, Object value) throws SocketException {}
}
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
This bug is easily seen with the simple test program below and either a profiler that shows open file handles or some Linux commands to do the same thing. I used lsof on Linux and OptimizeIt on Windows to see the problem and to see that my fix worked.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Looping on opening a DatagramChannel, getting the socket, and closing all resources should result in a file descriptor count that does not increase.
ACTUAL -
The file descriptor count increases with each iteration of the loop. If the handle limit is high enough and garbage collection happens soon enough then eventually the handle count will jump down as the stranded PlainDatagramSocketImpl objects are finalized. If not, then eventually new opens will fail with an error stating that there are too many open files.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.SocketException: Too many open files
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.net.DatagramSocket;
import java.nio.channels.*;
import java.io.IOException;
public class FDLeak {
public static void main(String[] args) throws IOException {
DatagramSocket so = null;
DatagramChannel ch = null;
for(int i = 0; i < 5; i++) {
System.out.println("Opening socket in 5 seconds");
try {
Thread.sleep(5000);
} catch(InterruptedException e) {}
ch = DatagramChannel.open();
try {
so = ch.socket();
} finally {
System.out.println("Closing socket in 5 seconds");
try {
Thread.sleep(5000);
} catch(InterruptedException e) {}
if(so != null) {
so.close();
}
try {
ch.close();
} catch(IOException ex) {}
so = null;
ch = null;
}
}
System.out.println("Exiting in 5 seconds");
try {
Thread.sleep(5000);
} catch(InterruptedException e) {}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
The only way around this problem is to replace sun.nio.ch.DatagramSocketAdaptor with your own implementation using -Xbootclasspath.
###@###.### 2005-03-28 06:33:56 GMT