保持对实现java.rmi.Remote
接口的对象的强引用,以使其保持可达性,即不符合垃圾回收的条件。
下面是一个简短的程序,演示了java.rmi.NoSuchObjectException
。该脚本是自包含的,在单个JVM中创建一个RMI注册表以及一个"客户端"和一个"服务器"。
只需复制此代码并将其保存在名为RMITest.java
的文件中。使用您选择的命令行参数进行编译和调用:
-gc
(默认)显式地指示JVM在服务器启动后但客户端连接到服务器之前尽力运行垃圾回收。这可能会导致Remote
对象被垃圾回收器回收,如果对Remote
对象的强引用被释放。当客户端在Remote
对象被回收后连接时,将观察到java.rmi.NoSuchObjectException
。
-nogc
不明确请求垃圾回收。这可能会导致Remote
对象仍然可以被客户端访问,无论是否持有或释放强引用除非在服务器启动和客户端调用之间存在足够的延迟,以便系统“自然”调用垃圾回收器并回收Remote
对象。
-hold
保留对Remote
对象的强引用。在这种情况下,类变量引用Remote
对象。
-release
(默认)将释放对Remote
对象的强引用。在这种情况下,方法变量引用Remote
对象。方法返回后,强引用将丢失。
-delay<S>
在服务器启动和客户端调用之间等待的秒数。插入延迟为垃圾回收器运行“自然”提供了时间。这模拟了一个最初“工作”的过程,但在一段重要的时间后失败。注意,在秒数前没有空格。例如:-delay5
将使客户端在服务器启动后5秒钟进行调用。
程序的行为可能因机器和JVM而异,因为诸如System.gc()
之类的东西只是提示,设置-delay<S>
选项是猜测垃圾收集器的行为。
在我的机器上,在编译javac RMITest.java
后,我看到这种行为:
$ java RMITest -nogc -hold
received: foo
$ java RMITest -nogc -release
received: foo
$ java RMITest -gc -hold
received: foo
$ java RMITest -gc -release
Exception in thread "main" java.rmi.NoSuchObjectException: no such object in table
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:178)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132)
at $Proxy0.remoteOperation(Unknown Source)
at RMITest.client(RMITest.java:69)
at RMITest.main(RMITest.java:46)
这里是源代码:
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import static java.util.concurrent.TimeUnit.*;
interface RemoteOperations extends Remote {
String remoteOperation() throws RemoteException;
}
public final class RMITest implements RemoteOperations {
private static final String REMOTE_NAME = RemoteOperations.class.getName();
private static final RemoteOperations classVariable = new RMITest();
private static boolean holdStrongReference = false;
private static boolean invokeGarbageCollector = true;
private static int delay = 0;
public static void main(final String... args) throws Exception {
for (final String arg : args) {
if ("-gc".equals(arg)) {
invokeGarbageCollector = true;
} else if ("-nogc".equals(arg)) {
invokeGarbageCollector = false;
} else if ("-hold".equals(arg)) {
holdStrongReference = true;
} else if ("-release".equals(arg)) {
holdStrongReference = false;
} else if (arg.startsWith("-delay")) {
delay = Integer.parseInt(arg.substring("-delay".length()));
} else {
System.err.println("usage: javac RMITest.java && java RMITest [-gc] [-nogc] [-hold] [-release] [-delay<seconds>]");
System.exit(1);
}
}
server();
if (invokeGarbageCollector) {
System.gc();
}
if (delay > 0) {
System.out.println("delaying " + delay + " seconds");
final long milliseconds = MILLISECONDS.convert(delay, SECONDS);
Thread.sleep(milliseconds);
}
client();
System.exit(0);
}
@Override
public String remoteOperation() {
return "foo";
}
private static void server() throws Exception {
final RemoteOperations methodVariable = new RMITest();
final RemoteOperations toBeStubbed = holdStrongReference ? classVariable : methodVariable;
final Remote remote = UnicastRemoteObject.exportObject(toBeStubbed, 0);
final Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
registry.bind(REMOTE_NAME, remote);
}
private static void client() throws Exception {
final Registry registry = LocateRegistry.getRegistry();
final Remote remote = registry.lookup(REMOTE_NAME);
final RemoteOperations stub = RemoteOperations.class.cast(remote);
final String message = stub.remoteOperation();
System.out.println("received: " + message);
}
}