java.rmi.NoSuchObjectException:テーブルにそのようなオブジェクトはありません
質問
非常に単純なRMIサーバーを作成していますが、ユニットテストで断続的なjava.rmi.NoSuchObjectExceptions
が表示されています。
同じオブジェクトに対してリモートメソッド呼び出しの文字列があり、最初のいくつかは通過しますが、後の呼び出しは失敗することがあります。間にあるサーバーオブジェクトの登録を解除するために何もしていません。
これらのエラーは常に表示されるわけではなく、ブレークポイントを設定すると表示されない傾向があります。デバッガーの実行が遅くなったため、競合状態が解消されるハイゼンバグはありますか?テストコードまたはサーバーコードでマルチスレッドは行われていません(ただし、RMIスタック内にある可能性があります)。
これをMac OS X 10.5(Java 1.5)でEclipseのJUnitプラグインを介して実行していますが、RMIサーバーとクライアントは両方とも同じJVMにあります。
これらの例外の原因は何ですか?
解決
java.rmi.Remote
インターフェースを実装するオブジェクトへの強い参照を保持して、到達可能、つまりガベージコレクションの対象外。
以下は、 <= > 。スクリプトは自己完結型で、RMIレジストリと<!> quot; client <!> quot;を作成します。および<!> quot; server <!> quot;単一のJVMで。
このコードを単にコピーして、java.rmi.NoSuchObjectException
という名前のファイルに保存します。コマンドライン引数を選択してコンパイルして呼び出します:
-
RMITest.java
(デフォルト)<!> quot;ベストエフォート<!> quot;サーバーの起動後、クライアントがサーバーに接続する前にガベージコレクターを実行します。これにより、-gc
オブジェクトへの強い参照がリリース された場合、Remote
オブジェクトがガベージコレクターによって回収される可能性があります。-nogc
オブジェクトが回収された後にクライアントが接続すると、-hold
が観察されます。 -
-release
ガベージコレクションを明示的に要求しないでください。これにより、サーバーの起動とクライアントの呼び出しの間に十分な delay がある場合を除き、強い参照が保持または解放されているかどうかに関係なく、クライアントは-delay<S>
オブジェクトにアクセス可能なままになります。システムが<!> quot;自然に<!> quot;ガベージコレクターを呼び出し、-delay5
オブジェクトを回収します。 -
System.gc()
javac RMITest.java
オブジェクトへの強い参照を保持します。この場合、クラス変数は<=>オブジェクトを参照します。 - <=> (デフォルト)<=>オブジェクトへの強い参照が解放されます。この場合、メソッド変数は<=>オブジェクトを参照します。メソッドが戻った後、強参照は失われます。
- <=> サーバーの起動からクライアントの呼び出しまで待機する秒数。遅延を挿入すると、ガベージコレクターが<!> quot; naturally。<!> quot;を実行する時間が与えられます。これは、<!> quot; works <!> quot;のプロセスをシミュレートします。最初は、かなりの時間が経過すると失敗します。秒数の前にスペースがないことに注意してください。例:<=>は、サーバーが起動してから5秒後にクライアントを呼び出します。
のようなものは単なるヒントであり、<=>オプションの設定はガベージコレクターの動作に関して推測ゲームであるため、
プログラムの動作はマシンごと、JVMごとに異なる可能性があります。
私のマシンでは、<=>をコンパイルした後、次の動作が見られます:
$ 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プロパティを有効にすることです( http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties.html ) そして、すべてのすばらしい情報がログウィンドウを流れていくのを見てください。これにより、毎回何が起きているかがわかります。
jos
同じ問題を抱えていますが、今では解決しました。解決策は簡単です。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...
}
したがって、<!> quot; serviceImpl <!> quot;を保護します。オブジェクトがGCされません。 CMIIW
上記の議論に欠けている点が1つあります。分散ガベージコレクション(DGC)と呼ばれるものがあります。分散オブジェクトへのローカルおよびリモートの参照が存在しない場合、GCはオブジェクトをメモリから削除できます。これを検証するための洗練されたアルゴリズムがあります。上記の優れたコードスニペットは、確かにDGCの有効性の良いデモンストレーションです。
何らかの形で機能のように見えるものは、設計された動作にすぎません!
フランク
コードを見ずにこの質問に答えることは困難です(ここで公開できないほど大きいと思います)。ただし、Occamのカミソリを使用すると、2つの可能性があります
- サーバーオブジェクトは何らかの方法で登録解除する必要があります
- ブレークポイントはエラーを停止するので、間違いなく競合状態です。
上記の2点を念頭に置いて、コードパスを慎重に検討することをお勧めします。
Spring Remoting(rmi)を使用していると、このエラーにぶつかりました。 私のサービスはガベージコレクションではありませんでした。
<!> quot; org.springframework <!> quotのデバッグロギングをオンにした後。サーバーがクライアントが接続しようとしたポートではなく、デフォルトのポート(1099)でサービスを登録していることを発見しました。
ポート単位のすべてが問題ないと思った<!> quot; java.rmi.server.logCalls = true <!> quot;クライアントが接続しようとしたときに、サーバー上に出力が表示されました。
このエラーが発生した場合、ポート(サービスおよびレジストリのポート)を再確認してください。
同じエラーが発生しましたが、おそらく他の(まだ不明な)理由のためです。
エクスポートされたオブジェクトをリモートインターフェイスの型にキャストしていましたが、名前へのバインド中に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);
}