Ответ 1
Хранить сильную ссылку на объект, реализующий интерфейс java.rmi.Remote
, чтобы он оставался достижимым, то есть не имеющим права на сбор мусора.
Ниже приведена короткая программа, демонстрирующая java.rmi.NoSuchObjectException
. script является автономным, создавая реестр RMI, а также "клиент" и "сервер" в одной JVM.
Просто скопируйте этот код и сохраните его в файле с именем RMITest.java
. Скомпилируйте и вызовите с помощью ваших аргументов командной строки:
-
-gc
(по умолчанию) Явное указание JVM сделать "лучшее усилие" для запуска сборщика мусора после запуска сервера, но до того, как клиент подключится к серверу. Вероятно, это приведет к тому, что объектRemote
будет возвращен сборщиком мусора, если сильная ссылка на объектRemote
будет выпущена. Ajava.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);
}
}