我正在使用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/