几周前我写了一个Java类,其行为如下:
代码:
public class MyClass
{
private static Map<Integer, MyClass> map;
private final int field;
static
{
map = new HashMap<>();
}
private MyClass(int field)
{
this.field = field;
}
public static MyClass get(int field)
{
synchronized (map)
{
return map.computeIfAbsent(field, MyClass::new);
}
}
}
这样我可以肯定,每个整数(作为字段)只存在一个对象。我当前担心的是,这会阻止GC收集我不再需要的对象,因为对象总是存储在地图中(存在引用)......
如果我像这样的函数写了一个循环:
public void myFunction() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
MyClass c = MyClass.get(i);
// DO STUFF
}
}
调用方法后,我最终会在内存中找到Integer.MAX_VALUE
个对象。有没有办法可以检查,是否存在对地图中对象的引用,否则将其删除?
答案 0 :(得分:3)
这看起来像the multiton pattern的典型情况:您希望给定密钥最多只有一个'String login_URL = "http://**.***.***.***/home/ubuntu/login.php";
个实例。但是,您似乎也想限制创建的实例数量。根据您的需要lazily instantiating MyClass
个实例,这很容易实现。此外,您希望清理未使用的实例:
有没有办法检查,是否存在对地图中对象的引用,否则将其删除?
这正是JVM的garbage collector所针对的;没有理由尝试实现自己的形式&#34;垃圾收集&#34;当Java核心库已经提供tools for marking certain references as "not strong"时,即只有在引用它的某个地方有一个强引用(即在Java中,一个&#34;正常&#34;引用)时才应引用给定对象。
Reference
个对象而不是MyClass
,您应该使用Map<Integer, MyClass>
或Map<Integer, WeakReference<MyClass>>
:WeakReference
和SoftReference
允许Map<Integer, SoftReference<MyClass>>
个实例如果没有对该对象的强引用(读取:&#34;正常&#34;),则指的是垃圾收集。两者之间的区别在于,前者在所有强引用消失后释放对下一个垃圾收集操作的引用,而后者仅在它必须&#34;时才释放引用,即在某些时刻方便JVM(见related SO question)。
另外,您无需同步整个MyClass
:您只需使用ConcurrentHashMap
(实现ConcurrentMap
),which handles multi-threading in a way much better than by locking all access to the entire map。因此,您的Map
可能如下所示:
MyClass.get(int)
最后,在上面的评论中,您提到过"prefer to cache maybe up to say 1000 objects and after that only cache, what is currently required/referenced"。虽然我个人认为它的原因很少(好),但是可以通过将eager instantiation添加到{{1}来对第一个&#34; † 1000个对象执行{{3}}地图创作:
private static final ConcurrentMap<Integer, Reference<MyClass>> INSTANCES = new ConcurrentHashMap<>();
public static MyClass get(final int field) {
// ConcurrentHashMap.compute(...) is atomic <https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction->
final Reference<MyClass> ref = INSTANCES.compute(field, (key, oldValue) -> {
final Reference<MyClass> newValue;
if (oldValue == null) {
// No instance has yet been created; Create one
newValue = new SoftReference<>(new MyClass(key));
} else if (oldValue.get() == null) {
// The old instance has already been deleted; Replace it with a
// new reference to a new instance
newValue = new SoftReference<>(new MyClass(key));
} else {
// The existing instance has not yet been deleted; Re-use it
newValue = oldValue;
}
return newValue;
});
return ref.get();
}
†如何定义哪些对象是&#34;第一个&#34;由你决定;在这里,我只是使用整数键的自然顺序,因为它适用于一个简单的例子。
答案 1 :(得分:1)
您检查缓存的功能非常有用。首先,正如您所说,它创建了所有缓存对象。其次,它迭代Integer.MAX_VALUE
次。
更好的是:
public void myFunction() {
for(MyClass c : map.values()) {
// DO STUFF
}
}
对于手头的问题:是否有可能找出对象是否有引用它?
是。有可能的。但你不会喜欢它。
http://docs.oracle.com/javase/1.5.0/docs/guide/jvmti/jvmti.html
jvmtiError
IterateOverReachableObjects(jvmtiEnv* env,
jvmtiHeapRootCallback heap_root_callback,
jvmtiStackReferenceCallback stack_ref_callback,
jvmtiObjectReferenceCallback object_ref_callback,
void* user_data)
遍历堆中所有可到达的对象。如果可以访问MyClass对象,那么,它是可以访问的。
当然,通过将对象存储在缓存中,您可以将其设置为可访问,因此您必须将缓存更改为WeakReference
,并查看是否可以从迭代中排除这些缓存。
并且您不再使用纯Java,并且所有VM可能都不支持jvmti
。
正如我所说,你不会喜欢它。