和单机系统类似, 分布式系统也需要自动清除不再有客户端引用的对象(remote object)。 远程对象的自动垃圾回收机制, 将程序员从深坑中解放出来, 不再需要人工跟踪client所引用的对象了. RMI的垃圾回收算法为 引用计数法(reference-counting), 和 Modula-3’s Network Objects 很相似.(详情请搜索 “Network Objects”)
为实现引用计数, RMI 运行时需要跟踪各JVM中的存活对象引用. 当一个对象引用进入JVM时, 它的引用次数加 1; client第一次引用某个对象时, 会向服务器发送一条 “referenced” 消息. 在本地JVM中, 如果释放某个引用一次, 则引用次数减 1。当最后一个引用被丢弃时, 向服务器发送一条 “unreferenced” 消息. 协议中有很多坑; 但大多数消息都是在维护 referenced 和 unreferenced 消息, 目的是确保这些对象不会被过早回收(prematurely collected)。
如果某个 remote object 不再被任何客户端引用, RMI运行时就会使用弱引用来指向它. 假如服务端也不存在其他强引用, 那么在垃圾收集时, 就有可能会将弱引用丢弃. 分布式垃圾收集算法, 与 client JVM 之间, 主要是通过常规引用(normal reference), 以及弱引用(weak reference)的方式来进行交互。
只要客户端还有引用指向 remote object, 就不应该回收服务器中的这个对象, 有可能还会通过该对象进行远程调用(remote call), 或者作为返回值传递给客户端. 服务端在传递 remote object 给 client 时,会添加客户JVM的标识, 然后放到 referenced set 中。 远程对象必须实现 java.rmi.server.Unreferenced
接口, 以支持 unreferenced 通知。当客户端发现没有任何引用指向 remote object 时, 就会调用该对象的 unreferenced
方法。unreferenced
方法可能会被调用多次, 只要客户端发现 references set 是空的就会调用一次。远程对象的回收, 只有当本地引用和远程引用都不存在时, 才会进行。
注意, 如果客户端和remote server object 之间发生网络分裂(network partition), 可能会造成过早回收(因为网络不通, server 会认为客户端崩溃了). 因为有过早回收(premature collection)的可能, 所以 remote reference 不能保证引用的完整性; 换句话说, 可能某个 remote reference 指向的对象并不存在了。这时候再使用这种 reference 就会抛出 RemoteException
, 这种情况必须由开发人员编写代码进行捕获和处理。
原文链接: https://docs.oracle.com/javase/8/docs/platform/rmi/spec/rmi-arch4.html
翻译日期: 2017年06月26日