对垃圾收集远程对象的弱引用
WeakReference to garbage collect Remote Objects
我正在使用 JGroups 作为分布式系统。我想在远程 JVM 上创建对象并像在本地创建一样使用它们。因此我使用 java.lang.reflect.Proxy 来包装 RPC 调用。这很像 RMI 行为。这很好用。
但现在我想在客户端 interface/proxy 不再使用时对远程对象进行垃圾回收。所以我想我可以通过使用 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:
-详细: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(Object Query language)
select * 来自包.classname
然后点击!在工具栏上
它将给出该类型的对象列表
右键单击对象 -> GC 根路径 -> 排除 soft/weak/Phantom 引用
如果可疑对象没有任何强引用,则它将显示 NULL 否则
您将获得有关谁持有可疑对象的强引用的信息。
我正在使用 JGroups 作为分布式系统。我想在远程 JVM 上创建对象并像在本地创建一样使用它们。因此我使用 java.lang.reflect.Proxy 来包装 RPC 调用。这很像 RMI 行为。这很好用。
但现在我想在客户端 interface/proxy 不再使用时对远程对象进行垃圾回收。所以我想我可以通过使用 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:
-详细: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(Object Query language) select * 来自包.classname 然后点击!在工具栏上 它将给出该类型的对象列表 右键单击对象 -> GC 根路径 -> 排除 soft/weak/Phantom 引用 如果可疑对象没有任何强引用,则它将显示 NULL 否则 您将获得有关谁持有可疑对象的强引用的信息。