java.rmi.NoSuchObjectException:表中没有这样的对象

发布于 2024-07-15 05:48:53 字数 387 浏览 12 评论 0原文

我正在编写一个非常简单的 RMI 服务器,并且在单元测试中看到间歇性的 java.rmi.NoSuchObjectExceptions 。

我对同一个对象进行了一串远程方法调用,虽然前几个调用成功,但后面的调用有时会失败。 我没有做任何事情来取消注册服务器对象。

这些错误并不总是出现,如果我设置断点,它们往往不会出现。 是那些Heisenbugs,当通过调试器减慢的执行来查看它们时,它们的竞争条件会消失吗? 我的测试或服务器代码中没有进行多线程(尽管可能在 RMI 堆栈内部?)。

我通过 Eclipse 的 JUnit 插件在 Mac OS X 10.5 (Java 1.5) 上运行它,并且 RMI 服务器和客户端都在同一个 JVM 中。

什么会导致这些异常?

I am writing a very simple RMI server, and I am seeing intermittent java.rmi.NoSuchObjectExceptions in the unit tests.

I have a string of remote method calls on the same object, and while the first few go through, the later ones will sometimes fail. I am not doing anything to unregister the server object in between.

These error do not appear always, and if I put in breakpoints they tend to not appear. Are those Heisenbugs, whose race conditions dissolve when looking at them through the slowed down execution of the debugger? There is no multi-threading going on in my test or server code (though maybe inside of the RMI stack?).

I am running this on Mac OS X 10.5 (Java 1.5) through Eclipse's JUnit plugin, and the RMI server and client are both in the same JVM.

What can cause these exceptions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

離殇 2024-07-22 05:48:53

保留对实现 java.rmi.Remote 接口的对象的强引用,以便它保持可访问,即不符合垃圾回收条件。

下面是一个简短的程序,演示了 java.rmi.NoSuchObjectException。 该脚本是独立的,在单个 JVM 中创建 RMI 注册表以及“客户端”和“服务器”。

只需复制此代码并将其保存在名为 RMITest.java 的文件中。 使用您选择的命令行参数进行编译和调用:

  • -gc(默认)显式指示 JVM 在服务器启动后“尽最大努力”运行垃圾收集器已启动,但在客户端连接到服务器之前。 如果对 Remote 对象的强引用被释放,这可能会导致垃圾收集器回收 Remote 对象。嗯>。 回收 Remote 对象后客户端连接时,会观察到 java.rmi.NoSuchObjectException
  • -nogc 不要显式请求垃圾回收。 这可能会导致Remote对象仍然可以被客户端访问,无论是否持有或释放强引用,除非服务器启动之间有足够的延迟客户端调用使得系统“自然地”调用垃圾收集器并回收Remote对象
  • -hold 保留对Remote 对象的强引用。 在本例中,类变量引用 Remote 对象。
  • -release(默认)将释放对Remote对象的强引用。 在这种情况下,方法变量引用 Remote 对象。 方法返回后,强引用就丢失了。
  • -delay 服务器启动和客户端调用之间等待的秒数。 插入延迟为垃圾收集器“自然”运行提供了时间。 这模拟了一个最初“有效”的过程,但在经过一段相当长的时间后失败。 请注意,秒数之前没有空格。 示例:-delay5 将使客户端在服务器启动后 5 秒调用。

程序行为可能会因机器和 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<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);
    }
}

Keep a strong reference to the object that implements the java.rmi.Remote interface so that it remains reachable, i.e. ineligible for garbage collection.

Below is a short program that demonstrates a java.rmi.NoSuchObjectException. The script is self-contained, creating an RMI registry as well as a "client" and a "server" in a single JVM.

Simply copy this code and save it in a file named RMITest.java. Compile and invoke with your choice of command line arguments:

  • -gc (default) Explicitly instruct the JVM to make "a best effort" to run the garbage collector after the server is started, but before the client connects to the server. This will likely cause the Remote object to be reclaimed by the garbage collector if the strong reference to the Remote object is released. A java.rmi.NoSuchObjectException is observed when the client connects after the Remote object is reclaimed.
  • -nogc Do not explicitly request garbage collection. This will likely cause the Remote object to remain accessible by the client regardless of whether a strong reference is held or released unless there is a sufficient delay between the server start and the client call such that the system "naturally" invokes the garbage collector and reclaims the Remote object.
  • -hold Retain a strong reference to the Remote object. In this case, a class variable refers to the Remote object.
  • -release (default) A strong reference to the Remote object will be released. In this case, a method variable refers to the Remote object. After the method returns, the strong reference is lost.
  • -delay<S> The number of seconds to wait between server start and the client call. Inserting a delay provides time for the garbage collector to run "naturally." This simulates a process that "works" initially, but fails after some significant time has passed. Note there is no space before the number of seconds. Example: -delay5 will make the client call 5 seconds after the server is started.

Program behavior will likely vary from machine to machine and JVM to JVM because things like System.gc() are only hints and setting the -delay<S> option is a guessing game with respect to the behavior of the garbage collector.

On my machine, after javac RMITest.java to compile, I see this behavior:

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

Here is the source code:

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);
    }
}
旧人九事 2024-07-22 05:48:53

需要考虑的其他一些问题 - 首先,您是否引用了对象实例,或者存根接口本身是否消失了? 如果某个对象实例消失了,那么出于通常的原因,它会被取消引用并被 GC 回收,但如果是接口,那么您的 RMI 服务器端点循环会由于某种原因退出。

到目前为止,我发现的最好的调试工具是打开 java.rmi.server.logCalls=true 属性(请参阅 http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties.html)
并观看所有精彩信息从您的日志窗口流下。 这每次都会告诉我发生了什么。

乔斯

Some other questions to consider - First are you referencing an object instance or is the stub interface itself gone? If some object instance is gone, its for the usual reasons, it got dereferenced and GC'd, but if it's the interface then your RMI server end point loop quit for some reason.

The best debugging tool I've found so far is to turn on the java.rmi.server.logCalls=true property (see http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties.html)
and watch all the wonderfull information stream down your log window. This tells me what's up every time.

jos

<逆流佳人身旁 2024-07-22 05:48:53

我有同样的问题,现在我已经解决了。 解决方案很简单,您必须创建强引用“对象”以避免对象被 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 回收。 CMIIW

I have the same problem and now I've solved it. The solution is simple, you MUST create strong reference 'object' to avoid the object being GC'd.

for example in your server 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...
}

So, it protects "serviceImpl" object from being GC'd. CMIIW

滥情哥ㄟ 2024-07-22 05:48:53

上述讨论中遗漏了一点。 有一种东西叫做分布式垃圾收集(DGC)。 如果没有对分布式对象的活动本地和远程引用,则允许 GC 从内存中删除该对象。 有一个复杂的算法可以验证这一点。 上面的代码片段确实很好地展示了 DGC 的有效性。

看起来像是功能的东西只不过是设计的行为!

坦率

there is one point missing in the above discussion. There is something that is called distributed garbage collection (DGC). If there are no living local and remote references to a distributed object the GC is allowed to remove the object from memory. There is a sophisticated algorithm to verify this. The nice code snippet from above is indeed a good demonstration of the effectiveness of the DGC.

What somehow looks like a feature is nothing but the designed behavior!

Frank

我们的影子 2024-07-22 05:48:53

如果不查看代码就很难回答这个问题(我猜代码太大,无法在此处发布)。 然而,使用奥卡姆剃刀,你有两种可能性

  • 服务器对象必须以某种方式取消注册
  • 由于断点阻止了错误,这绝对是一个竞争条件。

我建议您仔细检查代码路径,并牢记上述两点。

It's difficult to answer this question without looking at the code (which I guess will be big enough to not be publishable here). However, using Occam's razor, you have two possibilies

  • Server objects must be getting unregistered somehow
  • Since breakpoints stop the errors, it's definitely a race condition.

I would suggest you go over the code paths carefully keeping the two points above in mind.

心碎的声音 2024-07-22 05:48:53

在使用 spring remoting (rmi) 时,我遇到了这个错误。
我的服务没有被垃圾收集。

打开“org.springframework”的调试日志记录后,我发现我的服务器正在默认端口(1099)而不是客户端尝试连接的端口上注册服务。

我认为一切端口都正常,因为当客户端尝试连接时,“java.rmi.server.logCalls=true”确实在服务器上显示了一些输出。

收到此错误时,请仔细检查端口(服务和注册表端口)。

While using spring remoting (rmi) i bumped into this error.
My service wasn't garbage collected.

After turning on debug logging for "org.springframework" i discovered that my server was registering the service on the default port (1099) instead of the port the client was trying to connect to.

I thought everything port wise was ok cause "java.rmi.server.logCalls=true" did show some output on server when client was trying to connect.

When getting this error double check the ports (the service and registry one).

已下线请稍等 2024-07-22 05:48:53

遇到同样的错误,但可能是由于其他(但未知)原因。

我将导出的对象转换为远程接口的类型,然后在绑定到名称时得到 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);
}

Got the same error but probably for the other (yet unknown) reason.

I was casting exported object to the type of my remote interface and then while binding to name I was getting NoSuchObjectException. Removing casting fixed the problem.

Briefly:

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文