Details
-
Bug
-
Resolution: Fixed
-
P2
-
8
-
b63
-
x86
-
os_x
-
Verified
Description
FULL PRODUCT VERSION :
openjdk version "1.7.0-jdk7u6-b16"
OpenJDK Runtime Environment (build 1.7.0-jdk7u6-b16-20120704)
OpenJDK 64-Bit Server VM (build 23.2-b08, mixed mode)
A DESCRIPTION OF THE PROBLEM :
FileDescriptor keeps a hard reference to Closeables associated with the FD causing applications to leak memory. This is regression from version <= 1.7u5 caused by the fix to a different bug.
A very common pattern for systems that pool sockets is to ask for an outputstream along with each request. Long running processes with pooling will consume more and more memory until they run out.
If you execute the test case like this:
/Library/Java/JavaVirtualMachines/1.7.0u6.jdk/Contents/Home/bin/java -XX:+HeapDumpOnOutOfMemoryError -Xms32M -Xmx32M filedesc.Test
It will output:
100000
200000
300000
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid40237.hprof ...
Heap dump file created [42914849 bytes in 0.539 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.net.Socket.getOutputStream(Socket.java:912)
at filedesc.Test.main(Test.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Analyzing the heap dump you will find approximately:
324339 instances of class [B
323816 instances of class java.lang.Object
323731 instances of class java.net.SocketOutputStream
2079 instances of class [C
2052 instances of class java.lang.String
771 instances of class java.util.TreeMap$Entry
757 instances of class [S
570 instances of class java.lang.Class
509 instances of class [I
If you run the test case on any previous version of the JVM it will run indefinitely and not run out of memory.
REGRESSION. Last worked in version 7
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Make a socket, getOutputStream, let the outputstream go out of scope, GC, notice that it isn't collected and is being held by the FileDescriptor.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
You should be able to ask for an outputstream from a socket without closing it and without that outputstream leaking.
ACTUAL -
The FileDescriptor adds each SocketOutputStream to an ArrayList with a hard reference.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package filedesc;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
public static void main(String[] args) throws IOException {
final ServerSocket serverSocket = new ServerSocket(2000);
new Thread(new Runnable() {
public void run() {
try {
serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
int i = 0;
Socket socket = new Socket("localhost", 2000);
while(true) {
socket.getOutputStream();
if (++i % 100000 == 0) System.out.println(i);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Replace FileDescriptor.class with a previous version that works correctly. Here is a suggested fix that uses weak references:
https://gist.github.com/3041647
openjdk version "1.7.0-jdk7u6-b16"
OpenJDK Runtime Environment (build 1.7.0-jdk7u6-b16-20120704)
OpenJDK 64-Bit Server VM (build 23.2-b08, mixed mode)
A DESCRIPTION OF THE PROBLEM :
FileDescriptor keeps a hard reference to Closeables associated with the FD causing applications to leak memory. This is regression from version <= 1.7u5 caused by the fix to a different bug.
A very common pattern for systems that pool sockets is to ask for an outputstream along with each request. Long running processes with pooling will consume more and more memory until they run out.
If you execute the test case like this:
/Library/Java/JavaVirtualMachines/1.7.0u6.jdk/Contents/Home/bin/java -XX:+HeapDumpOnOutOfMemoryError -Xms32M -Xmx32M filedesc.Test
It will output:
100000
200000
300000
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid40237.hprof ...
Heap dump file created [42914849 bytes in 0.539 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.net.Socket.getOutputStream(Socket.java:912)
at filedesc.Test.main(Test.java:23)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Analyzing the heap dump you will find approximately:
324339 instances of class [B
323816 instances of class java.lang.Object
323731 instances of class java.net.SocketOutputStream
2079 instances of class [C
2052 instances of class java.lang.String
771 instances of class java.util.TreeMap$Entry
757 instances of class [S
570 instances of class java.lang.Class
509 instances of class [I
If you run the test case on any previous version of the JVM it will run indefinitely and not run out of memory.
REGRESSION. Last worked in version 7
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Make a socket, getOutputStream, let the outputstream go out of scope, GC, notice that it isn't collected and is being held by the FileDescriptor.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
You should be able to ask for an outputstream from a socket without closing it and without that outputstream leaking.
ACTUAL -
The FileDescriptor adds each SocketOutputStream to an ArrayList with a hard reference.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package filedesc;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Test {
public static void main(String[] args) throws IOException {
final ServerSocket serverSocket = new ServerSocket(2000);
new Thread(new Runnable() {
public void run() {
try {
serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
int i = 0;
Socket socket = new Socket("localhost", 2000);
while(true) {
socket.getOutputStream();
if (++i % 100000 == 0) System.out.println(i);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Replace FileDescriptor.class with a previous version that works correctly. Here is a suggested fix that uses weak references:
https://gist.github.com/3041647
Attachments
Issue Links
- relates to
-
JDK-8003833 Spurious NPE from Socket.getIn/OutputStream
-
- Closed
-
-
JDK-7105952 Improve finalisation for FileInputStream/FileOutputStream/RandomAccessFile
-
- Closed
-
-
JDK-7183209 Backout 7105952 changes for jdk7u
-
- Closed
-
-
JDK-8199039 FileDescriptor changes in JDK 8 causing OutOfMemory Errors
-
- Closed
-