【问题标题】:What type of connections "RMI TCP Connection(idle)" threads correspond to?“RMI TCP Connection(idle)”线程对应什么类型的连接?
【发布时间】:2012-06-06 11:42:03
【问题描述】:

我正在使用 jdk1.6 开发基于 RMI 的分布式系统。

我偶尔会看到在 RMI 运行时中发生的 ConcurrentModificationException 错误,当它无法序列化对象时。我可以通过同时更新从远程方法返回的对象轻松重现该异常。

但问题是我找不到这些电话的来源。 RMI 异常被写入stderr(在它退出远程对象方法后在运行时代码中的服务器端捕获),但客户端服务中没有匹配的异常(如果这是一个合法的远程调用,RemoteException 有适当的原因会被提升)。

这些异常的唯一不同之处在于它们发生在“RMI TCP Connection(idle)”线程上,而不是像“RMI TCP Connection()-”这样的线程上。

关于 RMI 中那些“空闲”线程是什么的任何线索?我在 openjdk 源代码中找不到这样的。

更新:我正在添加一个复制的异常堆栈跟踪,这是您通常在所描述的情况下看到的。

服务器端控制台显示:

Exception dispatching call to [-3534448f:12f54948b7f:-7fff, 349678755005857493] in thread "RMI TCP Connection(6)-x.x.x.x" at Thu Apr 14 16:15:13 BST 2011:
java.util.ConcurrentModificationException
    at java.util.ArrayList.writeObject(ArrayList.java:573)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
    at sun.rmi.server.UnicastRef.marshalValue(UnicastRef.java:274)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:315)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

向调用者抛出客户端大小异常:

java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
    java.io.EOFException
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:173)
    at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:178)
    at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132)
    at $Proxy0.getData(Unknown Source)
    at Clnt.main(Clnt.java:11)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:115)
Caused by: java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2553)
    at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1899)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1873)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1752)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:306)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:155)
    ... 9 more

【问题讨论】:

  • 如果RMI在返回值的序列化过程中得到一个运行时异常,那么协议中已经来不及序列化异常了,所以它被吞没了。您可以发布堆栈跟踪吗?
  • 我已经用两边的堆栈跟踪更新了帖子。实际的异常被吞没了,但是客户端仍然不能反序列化方法结果,所以它抛出了一个 RE 的子类,应该在客户端日志的某个地方看到。

标签: java rmi


【解决方案1】:

在 RMI 的连接池中创建名为“RMI TCP Connection(idle)”的线程。当使用其中之一时,用于执行的可运行将线程重命名为“RMI TCP Connection(n)”,其中n 是正在处理的连接号(序列号),并重命名为“空闲”在 runnable 的 finally 块中。因此,任何标记为“RMI TCP Connection(idle)”的跟踪都必须在可运行对象将其重命名为连接线程之前出现,或者在它被重命名回来之后出现。

不要问我这怎么可能。如果不是您的问题,您的问题的实际答案是不要在同时返回对象时修改它们;-)

【讨论】:

  • 哎哟!我怎么忽略了重命名位。现在命名是有意义的,但看起来更奇怪,因为它不应该处理任何处于空闲状态的消息。我希望我知道哪个对象是可变的,因为我们会针对潜在的可变结果进行防御性复制以防止这种情况发生。我希望通过识别此聊天的端点来获得一些线索。
  • 我发现了运行时异常如何泄漏到线程池中,并添加了解释作为答案。它确实解释了为什么线程处于空闲状态以及为什么到那时端点信息会丢失。所以我必须等待其中一个客户端连接失败并记录下来。可能是一个查询 bean 的 jconsole,所以它永远不会出现在日志中。
【解决方案2】:

我明白了。有两种情况会出现问题:

  1. 当封送返回值引发一些异常并且该异常又包含易于并发修改的对象时。 UnicastServerRef 尝试将原因写入(已经损坏的)返回流并导致 ConcurrentModificationException
  2. 当方法引发异常并且此异常无法使用ConcurrentModificationException(或任何其他运行时异常)序列化时。
    这个异常一直向上到堆栈,并被线程池捕获和记录,而不是 RMI 运行时(这就是为什么开头没有Exception dispatching call to 行)。这解释了线程名称处于空闲状态,因为从 RMI 的角度来看,它已经返回到池中。

这是真正的异常,实际上与再现的异常在第一行和实际调用跟踪方面显示的有点不同:

Exception in thread "RMI TCP Connection(idle)" java.util.ConcurrentModificationException
 at java.util.ArrayList.writeObject(ArrayList.java:573)
 at sun.reflect.GeneratedMethodAccessor6.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
 at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1461)
 at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
 at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
 at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
 at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
 at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
 at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
 at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
 at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1509)
 at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1474)
 at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1392)
 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1150)
 at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
 at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:343)
 at sun.rmi.transport.Transport$1.run(Transport.java:159)
 at java.security.AccessController.doPrivileged(Native Method)
 at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
 at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
 at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
 at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 at java.lang.Thread.run(Thread.java:619)

它没有为原始问题提供太多线索,所以我必须等待客户端异常出现在某个时候才能解决这个问题。
如果通过 jconsole 读取 JMX 属性时发生这种异常,它不会显示堆栈跟踪,但会显示属性值不可用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-13
    • 2014-01-13
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    相关资源
    最近更新 更多