Java-检查映射中是否存在对对象的引用
几周前,我编写了一个具有以下行为的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;
- 每个对象都包含一个最终整数字段
- 该类包含一个静态映射(键: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个对象。