Java - 检查Map中对象的引用是否存在

时间:2016-07-10 14:29:30

标签: java dictionary reference garbage-collection

几周前我写了一个Java类,其行为如下:

  • 每个对象包含一个最终整数字段
  • 该类包含静态Map(Key:Integer,Content:MyClass)
  • 每当实例化类的对象时,如果静态映射中已存在具有所需整数字段的对象,则执行查找:返回它,否则创建一个并将其放入映射中。

代码:

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个对象。有没有办法可以检查,是否存在对地图中对象的引用,否则将其删除?

2 个答案:

答案 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>>WeakReferenceSoftReference允许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

正如我所说,你不会喜欢它。