java.rmi.NoSuchObjectException: нет такого объекта в таблице

Я пишу очень простой сервер RMI, и я вижу прерывистые java.rmi.NoSuchObjectExceptions в модульных тестах.

У меня есть строка удаленных вызовов метода на одном и том же объекте, и в то время как первые несколько пройдут, более поздние будут иногда терпеть неудачу. Я ничего не делаю, чтобы отменить регистрацию объекта сервера между ними.

Эта ошибка не появляется всегда, и если я помещаю точки останова, они, как правило, не отображаются. Являются ли эти Heisenbugs, условия гонки которых растворяются при просмотре их через замедленное выполнение отладчика? В моем тестовом или серверном коде много многопоточности (хотя, возможно, внутри стека RMI?).

Я запускаю это в Mac OS X 10.5 (Java 1.5) через плагин JUnit от Eclipse, а сервер RMI и клиент находятся в одной JVM.

Что может вызвать эти исключения?

Храните ссылки на объект, реализующий интерфейс java.rmi.Remote чтобы он оставался доступным , то есть не имеющим права на garbage collection.

Ниже приведена короткая программа, которая демонстрирует java.rmi.NoSuchObjectException . Сценарий является самодостаточным, создавая реестр RMI, а также «клиент» и «сервер» в одной JVM.

Просто скопируйте этот код и сохраните его в файле RMITest.java . Скомпилируйте и вызовите с помощью ваших аргументов командной строки:

  • -gc (по умолчанию) Явное указание JVM сделать «лучшее усилие» для запуска сборщика мусора после запуска сервера, но до того, как клиент подключится к серверу. Вероятно, это приведет к тому, что объект Remote будет восстановлен сборщиком мусора, если будет выпущена сильная ссылка на Remote объект . java.rmi.NoSuchObjectException наблюдается, когда клиент подключается после восстановления объекта Remote .
  • -nogc Не запрашивать garbage collection явным образом. Вероятно, это приведет к тому, что объект Remote останется доступным клиентом независимо от того, удерживается или освобождается сильная ссылка, если между стартом сервера и вызовом клиента не существует достаточной задержки , так что система «естественно» вызывает сборщик мусора и восстанавливает Remote объект .
  • -hold Сохранить сильную ссылку на объект Remote . В этом случае переменная classа относится к Remote объекту.
  • -release (по умолчанию) Сильная ссылка на объект Remote будет выпущена. В этом случае переменная метода относится к Remote объекту. После возврата метода сильная ссылка теряется.
  • -delay Количество секунд ожидания между запуском сервера и вызовом клиента. Вставка задержки обеспечивает время, в течение которого сборщик мусора запускается «естественно». Это имитирует процесс, который «работает» изначально, но терпит неудачу после некоторого значительного времени. Обратите внимание, что нет пробела до количества секунд. Пример: -delay5 заставит клиента позвонить через 5 секунд после запуска сервера.

Поведение программы, вероятно, будет отличаться от машины к машине и JVM для JVM, потому что такие вещи, как System.gc() являются только подсказками и установка -delay – игра с угадыванием в отношении поведения сборщика мусора.

На моей машине, после компиляции 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]"); 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); } } 

Некоторые другие вопросы, которые нужно рассмотреть: во-первых, вы ссылаетесь на экземпляр объекта или сам интерфейс заглушки? Если какой-то экземпляр объекта ушел, его по обычным причинам он получил разыменованный и GC’d, но если это интерфейс, то конечная точка сервера RMI-сервера прекратит свое существование по какой-то причине.

Лучшим инструментом отладки, который я нашел до сих пор, является включение свойства java.rmi.server.logCalls = true (см. http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties .html ) и следите за прекрасным streamом информации в окне журнала. Это говорит мне, что происходит каждый раз.

Jos

У меня такая же проблема, и теперь я ее решил. Решение прост, вы ДОЛЖНЫ создать сильный ссылочный «объект», чтобы исключить объект GC’d.

например, в вашем classе сервера:

 ... private static ServiceImpl serviceImpl = null; public static void register (int port) { serviceImpl = new ServiceImpl(); Registry registry = LocateRegistry.createRegistry(port); registry.rebind ("serviceImpl", serviceImpl); } public static void main(String[] args) throws RemoteException, NotBoundException { register(1099); ...the rest of your code... } 

Таким образом, он защищает объект «serviceImpl» от GC’d. CMIIW

в приведенном выше обсуждении отсутствует одна точка. Есть что-то, что называется распределенной сборкой мусора (DGC). Если нет живых локальных и удаленных ссылок на распределенный объект, GC разрешено удалять объект из памяти. Для этого существует сложный алгоритм. Хороший fragment кода сверху действительно является хорошей демонстрацией эффективности DGC.

Что-то похоже на функцию – это не что иное, как продуманное поведение!

Фрэнк

Трудно ответить на этот вопрос, не глядя на код (который, я думаю, будет достаточно большим, чтобы его нельзя было публиковать здесь). Однако, используя бритву Оккама, у вас есть две возможности

  • Объекты сервера должны как-то незарегистрироваться
  • Поскольку точки останова останавливают ошибки, это определенно условие гонки.

Я бы посоветовал вам переходить по кодам, тщательно соблюдая приведенные выше два пункта.

Получена такая же ошибка, но, вероятно, для другой (пока неизвестной) причины.

Я передавал экспортированный объект в тип моего удаленного интерфейса, а затем при привязке к имени я получал NoSuchObjectException. Устранение кастинга устранило проблему.

Кратко:

 public interface MyRemoteInterface extedns Remote { ... } public class MyRemoteObject implements MyRemoteInterface { ... } public static MyRemoteObject obj = new MyRemoteObject(); public static void main(String[] args) { //removing cast to MyRemoteInterface fixes the problem this.obj = UnicastRemoteObject.exportObject((MyRemoteInterface) this.obj, 0); //unless the above cast is removed, this throws NoSuchObjectException occasionally LocateRegisry.getRegistry("127.0.0.1", 1099).bind("name", this.obj); } 
Interesting Posts

Чистые печатающие головки

Каков канонический способ проверки ошибок с использованием API-интерфейса CUDA?

Android, как определить элемент в списке с помощью флажка

Windows 7 новых приложений без фокуса

Почему «невозможно найти кодировщик для типа, хранящегося в наборе данных» при создании набора данных пользовательского classа case?

Как уменьшить размер исполняемого файла, созданного компилятором MinGW g ++?

Проводник не обновляется автоматически

Анимированный GIF в IE останавливается

Все окна, возвращающиеся к основному монитору после пробуждения от сна

Выберите диалог папки WPF

Как просмотреть веб-камеру в Vista?

Как присоединиться / объединить много mp3-файлов?

Автоматически добавлять комментарии к файлам PDF с регулярными выражениями

Как я могу отправить себе электронное письмо, которое становится текстовым файлом на моем жестком диске (или в Dropbox)?

@Resource vs @Autowired

Давайте будем гением компьютера.