因为产品安全的需求,需要将之前老代码的RMI服务端口绑定到127.0.0.1。解决办法是通过Spring导出服务时设置自己的RMISocketFactory。在验证代码时发现自己实现的RMISocketFactory在有大量RMI请求时,会将造成系统的资源泄漏,导致请求时无法创建本地线程。

通过jconsole一看,原来在大量RMI请求时,生成了大量的RMI线程并且不会及时释放掉,而不自定义socketfactory则没有此问题。

公关了2天,各种尝试,今天终于找到的了原因:其实Oracle的FAQ早有说明:

http://docs.oracle.com/javase/7/docs/technotes/guides/rmi/faq.html

A.5 Why does the Java RMI implementation create so many sockets when my application uses custom socket factories; or why are stubs (using a custom socket factory) that refer to the same remote object not equal; or why does the Java RMI implementation not reuse server-side ports when I use a custom server socket factory?

The Java RMI implementation attempts to reuse open sockets where possible for remote invocations. When a remote method is invoked on a stub that uses a custom socket factory, the Java RMI implementation will reuse an open connection (if any) as long as that socket was created by an equivalent socket factory. Since client socket factories are serialized to clients, a single client may have several distinct copies of the same logical socket factory. To ensure that the Java RMI implementation will reuse sockets created by custom socket factories, make sure your custom client socket factory classes implement the hashCode and equalsmethods appropriately. If the client socket factory does not implement these methods correctly, another ramification is that stubs (using the client socket factory) that refer to the same remote object will not be equal.

The Java RMI implementation attempts to reuse server-side ports as well. It will only do so if there is an existing server socket for the port created by an equivalent socket factory. Make sure the server socket factory class implements the hashCode and equals methods too.

If your socket factory has no instance state, a trivial implementation of the hashCode and equals methods are the following:

    public int hashCode() { return 57; }
    public boolean equals(Object o) { return this.getClass() == o.getClass(); }


实现了hashCode和equals后,问题解决,网上的例子,包括spring的文档都没说这个,坑爹啊,大家都没发现这个问题?


共享下调试代码,供后面有相同问题的人参考

调试代码使用了spring2.5.5编译调试,使用时需要将其和相应依赖的包导入classpath

坑爹的RMI Socket Facotry_第1张图片