When a BasicExporter does not have DGC enabled, after its export method has been invoked, it will maintain a strong chain of reachability to the impl even after the unexport method has been invoked. It seems wrong that the exporter should prevent the impl from getting garbage collected even after unexport.
The chain of reachability goes as follows:
BasicExporter ->
BasicExportTable.Entry ->
ObjectTable.Target ->
Remote (the impl)
This bug is demonstrated by the following test case; note that if the "exporter = null" line is uncommented, the test passes:
import java.lang.ref.WeakReference;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.ref.BasicExporter;
import java.rmi.server.Exporter;
public class Test2 implements Remote {
public static void main(String[] args) {
try {
Test2 obj = new Test2();
WeakReference wr = new WeakReference(obj);
Exporter exporter = new BasicExporter(null, null, false, true);
Remote stub = exporter.export(obj);
exporter.unexport(true);
obj = null;
// exporter = null;
flushRefs();
if (wr.get() != null) {
System.err.println("FAILED: unexported object not collected");
throw new RuntimeException(
"FAILED: unexported object not collected");
} else {
System.err.println("PASSED: unexported object collected");
}
} catch (RemoteException e) {
System.err.println(
"FAILED: RemoteException encountered: " + e.getMessage());
e.printStackTrace();
throw new RuntimeException("FAILED: RemoteException encountered");
}
}
/**
* Force desparate garbage collection so that all WeakReference instances
* will be cleared.
*/
private static void flushRefs() {
java.util.Vector chain = new java.util.Vector();
try {
while (true) {
int[] hungry = new int[65536];
chain.addElement(hungry);
}
} catch (OutOfMemoryError e) {
}
}
}
The chain of reachability goes as follows:
BasicExporter ->
BasicExportTable.Entry ->
ObjectTable.Target ->
Remote (the impl)
This bug is demonstrated by the following test case; note that if the "exporter = null" line is uncommented, the test passes:
import java.lang.ref.WeakReference;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.ref.BasicExporter;
import java.rmi.server.Exporter;
public class Test2 implements Remote {
public static void main(String[] args) {
try {
Test2 obj = new Test2();
WeakReference wr = new WeakReference(obj);
Exporter exporter = new BasicExporter(null, null, false, true);
Remote stub = exporter.export(obj);
exporter.unexport(true);
obj = null;
// exporter = null;
flushRefs();
if (wr.get() != null) {
System.err.println("FAILED: unexported object not collected");
throw new RuntimeException(
"FAILED: unexported object not collected");
} else {
System.err.println("PASSED: unexported object collected");
}
} catch (RemoteException e) {
System.err.println(
"FAILED: RemoteException encountered: " + e.getMessage());
e.printStackTrace();
throw new RuntimeException("FAILED: RemoteException encountered");
}
}
/**
* Force desparate garbage collection so that all WeakReference instances
* will be cleared.
*/
private static void flushRefs() {
java.util.Vector chain = new java.util.Vector();
try {
while (true) {
int[] hungry = new int[65536];
chain.addElement(hungry);
}
} catch (OutOfMemoryError e) {
}
}
}