Сохраняйте строгую ссылку на объект, который реализует интерфейс java.rmi.Remote
, чтобы он оставался достижимым , т.е. не подходящим для сборки мусора.
Ниже приведена короткая программа, демонстрирующая java.rmi.NoSuchObjectException
. Сценарий является автономным, создавая реестр RMI, а также «клиента» и «сервер» в одной JVM.
Просто скопируйте этот код и сохраните его в файле с именем RMITest.java
. Скомпилируйте и вызовите по вашему выбору аргументы командной строки:
-gc
(по умолчанию) Явно проинструктируйте JVM приложить «максимальные усилия» для запуска сборщика мусора после запуска сервера, но до того, как клиент подключится к серверу. Это может привести к тому, что сборщик мусора восстановит объект Remote
, если сильная ссылка на объект Remote
равна release . java.rmi.NoSuchObjectException
наблюдается, когда клиент подключается после восстановления объекта Remote
.
-nogc
Не запрашивать явным образом сборку мусора. Это, вероятно, приведет к тому, что объект Remote
останется доступным для клиента независимо от того, удерживается или освобождается ли строгая ссылка , если между запуском сервера и вызовом клиента не будет достаточной задержки , так система «естественно» вызывает сборщик мусора и возвращает объект Remote
.
-hold
Сохраните строгое указание на объект Remote
. В этом случае переменная класса ссылается на объект Remote
.
-release
(по умолчанию) Будет опубликована сильная ссылка на объект Remote
. В этом случае переменная метода ссылается на объект Remote
. После возврата метода сильная ссылка теряется.
-delay<S>
Количество секунд ожидания между запуском сервера и вызовом клиента. Вставка задержки дает время для сборщика мусора "естественно". Это моделирует процесс, который изначально «работает», но завершается неудачно по прошествии некоторого времени. Обратите внимание, что до количества секунд не должно быть пробела. Пример: -delay5
будет звонить клиенту через 5 секунд после запуска сервера.
Поведение программы, вероятно, будет отличаться от машины к машине и от JVM до 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); // stop RMI server thread
}
@Override
public String remoteOperation() {
return "foo";
}
private static void server() throws Exception {
// This reference is eligible for GC after this method returns
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);
}
}