我正在使用JGroups作为分布式系统。我想在远程JVM上创建对象,并像在本地创建对象一样使用它们。因此,我正在使用java.lang.reflect.Proxy包装RPC调用。这与RMI行为非常相似。这很好。

但是现在,如果不再使用客户端接口/代理,我想垃圾收集远程对象。因此,我认为我可以通过使用WeakReference来完成此工作,但我从未陷入过GC周期。我想念什么?

public class RMILikeWrapper {
    private static final ScheduledExecutorService garbageCollector = Executors.newSingleThreadScheduledExecutor();
    private final Map remoteObjects = new ConcurrentHashMap();
    private final Map<WeakReference, IdPointer> grabageTracker = new ConcurrentHashMap<>();
    private final ReferenceQueue rq = new ReferenceQueue();
    private final RpcDispatcher rpcDispatcher;
    private final long callTimeout = 10000L;

    private class IdPointer {
        public final Address address;
        public final String id;

        public IdPointer(Address address, String id) {
            this.address = address;
            this.id = id;
        }

        @Override
        public String toString() {
            return "IdPointer{" + "address=" + address + ", id='" + id + '\'' + '}';
        }
    }

    public RMILikeWrapper(Channel channel) {
        this.rpcDispatcher = new RpcDispatcher(channel, null, null, this);

        // enable garbage collecting
        garbageCollector.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("my GC ");

                Reference<?> ref;  //this should be our weak reference
                while((ref = rq.poll()) != null) {
                    // remove weak reference from the map
                    IdPointer garbage = grabageTracker.remove(ref);

                    System.out.println("found expired weak references: " + garbage);
                    // now we need to destroy the remote object too
                    try {
                        rpcDispatcher.callRemoteMethod(garbage.address, "purge", new Object[]{garbage.id},
                                new Class[]{String.class}, new RequestOptions(ResponseMode.GET_FIRST, callTimeout));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        },0,10, TimeUnit.SECONDS);
    }

    public <T>T createRemoteObject(Class<T> proxyInterface, Address targetNode, Class c, Object[] args, Class[] argTypes) {
        try {
            Object[] remoteArgs = new Object[4];
            remoteArgs[0] = UUID.randomUUID().toString();
            remoteArgs[1] = c;
            remoteArgs[2] = args;
            remoteArgs[3] = argTypes;

            rpcDispatcher.callRemoteMethod(targetNode, "addObject", remoteArgs,
                    new Class[]{String.class, Class.class, Object[].class, Class[].class},
                    new RequestOptions(ResponseMode.GET_FIRST, callTimeout));

            // now get in interface stub for this object
            return getRemoteObject(targetNode, remoteArgs[0].toString(), proxyInterface);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    // Operation triggerd by RPC
    public void addObject(String id, Class c, Object[] args, Class[] parameterTypes) throws Exception {
        remoteObjects.put(id, c.getConstructor(parameterTypes).newInstance(args));
    }

    // Operation triggerd by RPC
    public Object invoke(String id, String methodName, Object[] args, Class[] argTypes) throws Exception {
        Object ro = remoteObjects.get(id);
        return ro.getClass().getMethod(methodName, argTypes).invoke(ro, args);
    }

    // Operation triggerd by RPC
    public void purge(String id) {
        System.out.println("garbage collecting: " + id);

        //return remoteObjects.remove(id) != null;
        remoteObjects.remove(id);
    }

    public <T>T getRemoteObject(final Address nodeAdress, final String id, final Class<T> clazz) {
        if (!clazz.isInterface()) throw new RuntimeException("Class has to be an interface!");

        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke (Object proxy, Method method, Object[] args) throws Throwable {
                Object[] remoteArgs = new Object[4];
                remoteArgs[0] = id;
                remoteArgs[1] = method.getName();
                remoteArgs[2] = args;
                remoteArgs[3] = method.getParameterTypes();

                // remote call
                return rpcDispatcher.callRemoteMethod(nodeAdress, "invoke",
                        remoteArgs, new Class[]{String.class, String.class, Object[].class, Class[].class},
                        new RequestOptions(ResponseMode.GET_FIRST, callTimeout));
            }
        };

        T result = (T) Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class[]{clazz},
                handler);

        // use weak pointers to the proxy object here and if one is garbage collected, purge the remote object as well
        WeakReference<T> weakReference = new WeakReference<>(result, rq);
        grabageTracker.put(weakReference, new IdPointer(nodeAdress, id));

        return result;
    }

    public static void main(String[] args) throws Exception {
        Channel channel = new JChannel();
        channel.connect("test-cluster");

        List<Address> members = channel.getView().getMembers();
        RMILikeWrapper w = new RMILikeWrapper(channel);

        if (members.size() > 1) {
            System.out.println("send to " + members.get(0));
            FooInterface remoteObject = w.createRemoteObject(FooInterface.class, members.get(0), FooImpl.class, null, null);
            System.out.println(remoteObject.doSomething("Harr harr harr"));
            remoteObject = null;
        }

        System.out.println(channel.getView().getMembers());
    }
}

最佳答案

使用以下方法,您可以确定GC在弱引用上的行为。
选项1:

-verbose:gc

每当GC发挥作用时,此参数都会记录GC行为。当您要检查GC是否生效时,可以使用日志文件,可以从GC日志中检查它。对于交互式GC分析,请尝试使用http://www.ibm.com/developerworks/java/jdk/tools/gcmv/记录日志

选项2:

收集堆转储和用户事件并将其加载到https://www.ibm.com/developerworks/java/jdk/tools/memoryanalyzer/

在OQL部分写OQL(对象查询语言)
从包中选择* .classname
然后点击!在工具栏上
它将给出该类型的对象列表
右键单击对象-> GC根目录路径->排除软/弱/幻像引用
如果可疑对象没有任何强引用,则将显示NULL否则
您将获得有关谁在可疑对象上拥有强引用的信息。

关于java - 对垃圾收集远程对象的WeakReference,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31964762/

10-09 22:49