java.rmi.NoSuchObjectException: 表中没有这样的对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/645208/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java.rmi.NoSuchObjectException: no such object in table
提问by Thilo
I am writing a very simple RMI server, and I am seeing intermittent java.rmi.NoSuchObjectExceptions
in the unit tests.
我正在编写一个非常简单的 RMI 服务器,我java.rmi.NoSuchObjectExceptions
在单元测试中看到断断续续的情况。
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?).
这些错误并不总是出现,如果我设置断点,它们往往不会出现。那些 Heisenbugs 是不是在通过调试器的缓慢执行查看它们时竞争条件消失了?在我的测试或服务器代码中没有进行多线程(尽管可能在 RMI 堆栈内部?)。
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.
我通过 Eclipse 的 JUnit 插件在 Mac OS X 10.5 (Java 1.5) 上运行它,并且 RMI 服务器和客户端都在同一个 JVM 中。
What can cause these exceptions?
什么会导致这些异常?
采纳答案by Greg Mattes
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.
保持对实现java.rmi.Remote
接口的对象的强引用,使其保持可达,即不适合垃圾收集。
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.
下面是一个简短的程序,它演示了一个java.rmi.NoSuchObjectException
. 该脚本是独立的,在单个 JVM 中创建 RMI 注册表以及“客户端”和“服务器”。
Simply copy this code and save it in a file named RMITest.java
. Compile and invoke with your choice of command line arguments:
只需复制此代码并将其保存在名为RMITest.java
. 使用您选择的命令行参数编译和调用:
-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 theRemote
object to be reclaimed by the garbage collector if the strong reference to theRemote
object is released. Ajava.rmi.NoSuchObjectException
is observed when the client connects after theRemote
object is reclaimed.-nogc
Do not explicitly request garbage collection. This will likely cause theRemote
object to remain accessible by the client regardless of whether a strong reference is held or released unless there is a sufficient delaybetween the server start and the client call such that the system "naturally" invokes the garbage collector and reclaims theRemote
object.-hold
Retain a strong reference to theRemote
object. In this case, a class variable refers to theRemote
object.-release
(default) A strong reference to theRemote
object will be released. In this case, a method variable refers to theRemote
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.
-gc
(默认)显式指示 JVM 在服务器启动后、但在客户端连接到服务器之前“尽最大努力”运行垃圾收集器。如果对对象的强引用被释放,这可能会导致Remote
对象被垃圾收集器回收。回收对象后客户端连接时观察到A。Remote
java.rmi.NoSuchObjectException
Remote
-nogc
不要显式请求垃圾收集。这很可能会导致Remote
客户端仍然可以访问对象,无论强引用是被持有还是释放,除非在服务器启动和客户端调用之间有足够的延迟,以便系统“自然地”调用垃圾收集器并回收Remote
对象。-hold
保留对Remote
对象的强引用。在这种情况下,类变量引用Remote
对象。-release
(默认)对Remote
对象的强引用将被释放。在这种情况下,方法变量引用Remote
对象。方法返回后,强引用丢失。-delay<S>
在服务器启动和客户端调用之间等待的秒数。插入延迟为垃圾收集器“自然”运行提供了时间。这模拟了一个最初“有效”的过程,但在经过一段时间后失败。注意秒数前没有空格。示例:-delay5
将在服务器启动后 5 秒进行客户端调用。
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.
程序行为可能会因机器和 JVM 的不同而有所不同,因为诸如此类System.gc()
的事情只是提示,而设置-delay<S>
选项是关于垃圾收集器行为的猜谜游戏。
On my machine, after javac RMITest.java
to compile, I see this behavior:
在我的机器上,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)
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);
}
}
回答by talonx
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.
我建议您仔细检查代码路径,牢记上述两点。
回答by jottos
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.
需要考虑的其他一些问题 - 首先是引用对象实例还是存根接口本身消失了?如果某个对象实例消失了,通常是因为它被取消引用和垃圾回收,但是如果它是接口,那么你的 RMI 服务器端点循环会由于某种原因退出。
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.
到目前为止我发现的最好的调试工具是打开 java.rmi.server.logCalls=true 属性(参见http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties .html) 并在日志窗口中观看所有精彩的信息流。这每次都告诉我发生了什么。
jos
乔斯
回答by Frank Z
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.
上述讨论中遗漏了一点。有一种叫做分布式垃圾收集 (DGC) 的东西。如果没有对分布式对象的实时本地和远程引用,则允许 GC 从内存中删除该对象。有一个复杂的算法来验证这一点。上面漂亮的代码片段确实很好地证明了 DGC 的有效性。
What somehow looks like a feature is nothing but the designed behavior!
以某种方式看起来像功能的东西只不过是设计的行为!
Frank
坦率
回答by TheJanOnline
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.
我将导出的对象转换为远程接口的类型,然后在绑定到名称时遇到 NoSuchObjectException。删除铸造解决了这个问题。
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);
}
回答by Fahmi
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
因此,它保护“serviceImpl”对象不被 GC 处理。CMIIW
回答by Tinus Tate
While using spring remoting (rmi) i bumped into this error. My service wasn't garbage collected.
在使用弹簧远程处理(rmi)时,我遇到了这个错误。我的服务没有被垃圾收集。
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.
打开“org.springframework”的调试日志后,我发现我的服务器正在默认端口(1099)上注册服务,而不是客户端试图连接的端口。
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.
我认为一切端口明智都可以,因为当客户端尝试连接时,“java.rmi.server.logCalls=true”确实在服务器上显示了一些输出。
When getting this error double check the ports (the service and registry one).
收到此错误时,请仔细检查端口(服务和注册表之一)。