java.rmi.NoSuchObjectException: brak takiego obiektu w tabeli

Piszę bardzo prosty serwer RMI i widzę przerywane java.rmi.NoSuchObjectExceptions w testach jednostkowych.

Mam ciąg zdalnych wywołań metod na tym samym obiekcie i podczas gdy pierwsze kilka przechodzi przez, późniejsze czasami zawodzą. Nie zrobię nic, aby wyrejestrować obiekt serwera pomiędzy.

Te błędy nie pojawiają się zawsze, a jeśli dodam punkty przerwania, zwykle nie pojawiają się. Są te Heisenbugs, których warunki rasowe rozpuszczają się patrząc na nich przez spowolnienie wykonania debuggera? Nie ma wielowątkowości w kodzie testowym lub serwerowym (choć może wewnątrz stosu RMI?).

Uruchamiam to na Mac OS X 10.5 (Java 1.5) poprzez wtyczkę JUnit Eclipse, a serwer i klient RMI są w tym samym JVM.

Co może powodować te wyjątki?
Author: Greg Mattes, 2009-03-14

6 answers

Zachowaj silne odniesienie do obiektu, który implementuje interfejs java.rmi.Remote tak, aby pozostał osiągalny, tzn. nie kwalifikujący się do zbierania śmieci.

Poniżej krótki program, który demonstruje java.rmi.NoSuchObjectException. Skrypt jest samowystarczalny, tworząc rejestr RMI oraz "klienta" i "serwera" w jednym JVM.

Po prostu skopiuj ten kod i zapisz go w pliku o nazwie RMITest.java. Skompiluj i wywołaj za pomocą wybranego wiersza poleceń argumenty:

  • -gc (domyślnie) wyraźnie instruuje JVM, aby "postarał się najlepiej" uruchomić garbage collector po uruchomieniu serwera, ale zanim klient połączy się z serwerem. Spowoduje to prawdopodobnie, że obiekt Remote zostanie odzyskany przez garbage collector , jeśli silne odniesienie do obiektu Remote zostanie zwolnione. A {[3] } jest obserwowane, gdy klient łączy się po odzyskaniu obiektu Remote.
  • -nogc nie jawnie Zażądaj usunięcia śmieci. Prawdopodobnie spowoduje to, że obiekt Remote pozostanie Dostępny dla klienta bez względu na to, czy zostanie utrzymane lub zwolnione silne odniesienie , chyba że istnieje wystarczające opóźnienie między startem serwera a wywołaniem klienta, tak że system "naturalnie" wywoła garbage collector i odzyskuje obiekt Remote .
  • -hold Zachowaj silne odniesienie do obiektu Remote. W tym przypadku zmienna klasy odnosi się do Remote obiekt.
  • -release (domyślnie) zostanie wydane silne odniesienie do obiektu Remote. W tym przypadku zmienna metody odnosi się do obiektu Remote. Po powrocie metody silne odniesienie jest tracone.
  • -delay<S> liczba sekund oczekiwania między uruchomieniem serwera a wywołaniem klienta. Wstawianie opóźnienia zapewnia czas dla garbage collector uruchomić " naturalnie."To symuluje proces, który początkowo "działa" , ale zawodzi po pewnym czasie minęło. Uwaga nie ma spacji przed liczbą sekund. Przykład: -delay5 wykona wywołanie klienta 5 sekund po uruchomieniu serwera.

Zachowanie programu prawdopodobnie będzie się różnić w zależności od Maszyny i JVM do JVM, ponieważ rzeczy takie jak System.gc() są tylko podpowiedziami, a ustawienie opcji -delay<S> jest zgadywanką w odniesieniu do zachowania garbage collector.

Na mojej maszynie, po javac RMITest.java skompilować, widzę takie zachowanie:

$ 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)

Oto źródło kod:

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);
    }
}
 64
Author: Greg Mattes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-23 15:30:11

Kilka innych pytań do rozważenia - najpierw odwołujesz się do instancji obiektu, czy sam interfejs stub zniknął? Jeśli jakaś instancja obiektu zniknie, to z zwykłych powodów, to się dereferenced i GC' D, ale jeśli jest to interfejs, to Twój RMI server end point loop zamknąć z jakiegoś powodu.

Najlepsze narzędzie do debugowania, jakie do tej pory znalazłem, to włączenie Javy.rmi.serwer.logCalls = true property (see http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties.html ) oraz Oglądaj wszystkie wspaniałe informacje w oknie dziennika. To mi mówi, co się dzieje za każdym razem.

Jos

 8
Author: jottos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-14 08:58:55

Mam ten sam problem i teraz go rozwiązałem. Rozwiązanie jest proste, musisz utworzyć silny "obiekt odniesienia", aby uniknąć GC ' d obiektu.

Na przykład w klasie serwera:

...
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...
}

Chroni więc obiekt "serviceImpl" przed byciem GC ' d. CMIIW

 2
Author: Fahmi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 03:06:45

W powyższej dyskusji brakuje jednego punktu. Jest coś, co nazywa się distributed garbage collection (DGC). Jeśli nie ma żywych lokalnych i zdalnych odniesień do rozproszonego obiektu, GC może usunąć obiekt z pamięci. Istnieje zaawansowany algorytm, który to zweryfikuje. Ładny fragment kodu z góry jest rzeczywiście dobrym dowodem skuteczności DGC.

To, co w jakiś sposób wygląda jak funkcja, to nic innego jak zaprojektowane zachowanie!

Frank

 1
Author: Frank Z,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-04 07:07:13

Trudno jest odpowiedzieć na to pytanie bez spojrzenia na kod (który chyba będzie wystarczająco duży, aby nie był publikowany tutaj). Jednakże, używając brzytwy Occama, masz dwie możliwości

  • Obiekty serwera muszą być jakoś niezarejestrowane
  • ponieważ punkty przerwania zatrzymują błędy, jest to zdecydowanie warunek wyścigu.

Sugerowałbym, abyś przejrzał ścieżki kodu uważnie pamiętając o dwóch powyższych punktach.

 0
Author: talonx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-14 04:51:40

Otrzymałem ten sam błąd, ale prawdopodobnie z innego (jeszcze nieznanego) powodu.

Rzucałem wyeksportowany obiekt na typ mojego zdalnego interfejsu, a następnie wiążąc się z nazwą otrzymywałem NoSuchObjectException. Usunięcie odlewania naprawiło problem.

Krótko:

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);
}
 -1
Author: TheJanOnline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-20 11:15:18