문제

매우 간단한 RMI 서버를 작성하고 있으며 간헐적으로보고 있습니다. java.rmi.NoSuchObjectExceptions 단위 테스트에서.

나는 같은 객체에 일련의 원격 메소드 호출이 있고, 처음 몇 개는 지나가는 동안, 나중에는 때때로 실패 할 것입니다. 나는 그 사이의 서버 객체를 등록하지 않기 위해 아무것도하지 않습니다.

이러한 오류는 항상 나타나지 않으며, 중단 점을 넣으면 나타나지 않는 경향이 있습니다. 디버거의 속도 저하 실행을 통해 인종 조건이 해산되는 Heisenbugs입니까? 내 테스트 또는 서버 코드에는 멀티 스레딩이 진행되지 않습니다 (아마도 RMI 스택 내부에있을 수도 있습니까?).

Eclipse의 Junit 플러그인을 통해 Mac OS X 10.5 (Java 1.5)에서 이것을 실행하고 있으며 RMI 서버와 클라이언트는 모두 동일한 JVM에 있습니다.

이러한 예외를 일으킬 수있는 것은 무엇입니까?

도움이 되었습니까?

해결책

구현하는 물체에 대한 강력한 참조를 유지 java.rmi.Remote 남아 있도록 인터페이스 도달 할 수 있습니다, 즉 쓰레기 수집에 부적합합니다.

아래는 a를 보여주는 짧은 프로그램입니다 java.rmi.NoSuchObjectException. 스크립트는 자체 포함되어 단일 JVM에서 "클라이언트"및 "서버"뿐만 아니라 RMI 레지스트리를 만듭니다.

이 코드를 복사하여 이름이 지정된 파일에 저장하십시오. RMITest.java. 선택한 명령 줄 인수를 컴파일하고 호출하십시오.

  • -gc (기본값) 서버가 시작된 후에는 쓰레기 수집기를 실행하기 위해 "최선의 노력"을하도록 JVM에 명시 적으로 지시하지만 클라이언트가 서버에 연결하기 전에. 이것은 아마도 Remote 쓰레기 수집기가 되 찾는 물체 에 대한 강한 언급이 있다면 Remote 대상은 출시된. ㅏ 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);
    }
}

다른 팁

고려해야 할 몇 가지 질문 - 먼저 객체 인스턴스를 참조하고 있습니까? 아니면 스터브 인터페이스 자체가 사라 졌습니까? 일부 객체 인스턴스가 사라지면 일반적인 이유가 있으므로 불쾌감을 느끼고 GC를 사용했지만 인터페이스라면 RMI 서버 엔드 포인트 루프가 어떤 이유로 송금됩니다.

지금까지 찾은 최고의 디버깅 도구는 java.rmi.server.logcalls = True Property (참조)를 켜는 것입니다. http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties.html)) 그리고 모든 멋진 정보가 로그 창 아래로 스트림을 시청하십시오. 이것은 매번 무슨 일이 일어나고 있는지 알려줍니다.

조스

나는 같은 문제가 있고 이제 그것을 해결했습니다. 솔루션은 간단합니다. 객체가 GC를 피하기 위해 강력한 기준 '객체'를 만들어야합니다.

예를 들어 서버 클래스에서 :

...
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 (Distributed Garbage Collection)라고 불리는 것이 있습니다. 분산 객체에 대한 살아있는 로컬 및 원격 참조가없는 경우 GC는 메모리에서 객체를 제거 할 수 있습니다. 이를 확인하기위한 정교한 알고리즘이 있습니다. 위의 멋진 코드 스 니펫은 실제로 DGC의 효과에 대한 좋은 데모입니다.

어떻게 든 기능처럼 보이는 것은 설계된 행동 일뿐입니다!

솔직한

코드를 보지 않고이 질문에 대답하기는 어렵습니다 (여기에 게시 할 수 없을 정도로 충분히 크다고 생각합니다). 그러나 Occam의 면도기를 사용하면 두 가지 가능성이 있습니다.

  • 서버 객체는 어떻게 든 등록되지 않아야합니다
  • 중단 점은 오류를 중지하기 때문에 분명히 인종 조건입니다.

두 지점을 위의 두 지점을주의 깊게 염두에두고 코드 경로를 살펴 보는 것이 좋습니다.

Spring Remoting (RMI)을 사용하는 동안이 오류에 부딪쳤다. 내 서비스는 쓰레기가 수집되지 않았습니다.

"org.springframework"에 대한 디버그 로깅을 켜면 서버가 클라이언트가 연결하려는 포트 대신 기본 포트 (1099)에 서비스를 등록하고 있음을 발견했습니다.

나는 포트 현명한 모든 것이 정상이라고 생각했다고 생각했다.

이 오류가 발생하면 포트 (서비스 및 레지스트리 1)를 두 번 확인하십시오.

같은 오류가 있었지만 아마도 다른 (아직 알려지지 않은) 이유에 대해서는 아마도 오류가 발생했습니다.

나는 원격 인터페이스의 유형에 내보내기 객체를 캐스팅 한 다음 이름으로 바인딩하는 동안 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);
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top