Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java-检查映射中是否存在对对象的引用_Java_Dictionary_Reference_Garbage Collection - Fatal编程技术网

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

Java-检查映射中是否存在对对象的引用,java,dictionary,reference,garbage-collection,Java,Dictionary,Reference,Garbage Collection,几周前,我编写了一个具有以下行为的Java类: 每个对象都包含一个最终整数字段 该类包含一个静态映射(键:Integer,内容:MyClass) 每当实例化类的对象时,如果静态映射中已经存在具有所需整数字段的对象,则会进行查找:返回该对象,否则创建一个对象并将其放入映射中 作为代码: public class MyClass { private static Map<Integer, MyClass> map; private final int field;

几周前,我编写了一个具有以下行为的Java类:

  • 每个对象都包含一个最终整数字段
  • 该类包含一个静态映射(键:Integer,内容: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);
        }
    }
}
公共类MyClass
{
私有静态地图;
私有最终整型字段;
静止的
{
map=新的HashMap();
}
私有MyClass(整型字段)
{
this.field=字段;
}
公共静态MyClass get(整型字段)
{
同步(地图)
{
返回map.computeFabSent(字段,MyClass::new);
}
}
}
通过这种方式,我可以确定每个整数只存在一个对象(作为字段)。我现在担心的是,这将阻止GC收集我不再需要的对象,因为对象总是存储在映射中(存在引用)

如果我写了一个像这样的循环函数:

public void myFunction() {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
       MyClass c = MyClass.get(i);
       // DO STUFF
    } 
}
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();
}
public void myFunction(){
对于(int i=0;i

调用该方法后,我会在内存中找到
Integer.MAX\u VALUE
对象。是否有一种方法可以检查对映射中对象的引用是否存在,并以其他方式删除它们?

这看起来像一个典型的例子:对于给定的键,您希望最多有一个
MyClass
实例。但是,您似乎还希望限制创建的实例数量。这很容易通过
MyClass
实例实现,因为您需要它们。此外,您还希望清理未使用的实例:

有没有办法检查对地图中对象的引用是否存在,或者删除它们

这正是JVM的用途;当Java核心库已经提供时,没有理由尝试实现您自己形式的“垃圾收集”,也就是说,只有当某个地方有一个强引用(即Java中的“普通”引用)引用某个给定对象时,才应该引用该对象

使用对象实现 您应该使用
映射
映射
:而不是
映射
:如果没有对对象的强(读:“正常”)引用,则允许垃圾收集它们引用的
MyClass
实例。两者之间的区别在于,前者在所有强引用消失后在下一个垃圾收集操作上释放引用,而后者仅在“必须”时释放引用,即在JVM方便的某个点(请参阅)

另外,不需要同步整个
映射:您只需使用一个(实现的)。因此,您的
MyClass.get(int)
可能如下所示:

public void myFunction() {
    for (int i = 0; i < Integer.MAX_VALUE; i++) {
       MyClass c = MyClass.get(i);
       // DO STUFF
    } 
}
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();
}


†您如何定义哪些对象是“第一”对象取决于您;在这里,我只是使用整数键的自然顺序,因为它适用于一个简单的示例。

您用于检查缓存的函数非常有用。首先,正如您所说,它创建所有缓存对象。其次,它迭代
Integer.MAX_VALUE

最好是:

public void myFunction() {
    for(MyClass c : map.values()) {
       // DO STUFF
    } 
}
现在的问题是:是否有可能找出一个对象是否有对它的引用

对。这是可能的。但你不会喜欢的

循环堆中所有可访问的对象。如果MyClass对象是可访问的,那么它是可访问的

当然,通过将对象存储在缓存中,可以使其可访问,因此必须将缓存更改为
WeakReference
s,并查看是否可以从迭代中排除这些对象

而且您不再使用纯Java,
jvmti
可能不受所有VM的支持


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

通常,这种技术用于具有某种缓存,以避免在已经创建对象时创建对象。这不是你的目标吗?因为在这种情况下,它们在程序结束前保持分配是有意义的。您可能需要使用软引用或弱引用。例如,请参见,或者使用第三方缓存库,例如,请参见。@LorisSecuro是的,这是我的目标。但我更愿意缓存最多1000个对象,然后只缓存当前需要/引用的对象。@Andreas谢谢你,我会看一看:)@John也许你应该试试guava cache函数(最多迭代
Integer.MAX_VALUE
)只是一个例子,当缓存可能失败时,我很清楚如何迭代
java.util.map
。如果你感兴趣,你可以看看上面的答案。谢谢,这真的很酷!但是由于键是
整数
-对象,它们不也需要是
软引用
吗?
软引用
不能像
(Hash)映射
键那样工作,因为它没有实现
Hash()
equals()
。此外,同样的对象可能会在你的应用程序中到处使用,这意味着密钥被自动清除的可能性很小,因为它没有在地图以外的任何地方被引用……但是,如果你想要一个缓存,当它的密钥没有在其他地方被引用时,它会被清除,我在上面看到了您的评论,并添加了一个示例,说明如何主动初始化“第一个”1000个对象。