Java WeakHashMap是在不断增长,还是在清除垃圾钥匙?

Java WeakHashMap是在不断增长,还是在清除垃圾钥匙?,java,garbage-collection,weakhashmap,Java,Garbage Collection,Weakhashmap,我试图将其用作弱引用的并发引用 this.subscribers = Collections.synchronizedSet( Collections.newSetFromMap( new WeakHashMap <>() ) ); this.subscribers= Collections.

我试图将其用作弱引用的并发引用

    this.subscribers =
            Collections.synchronizedSet(
                    Collections.newSetFromMap(
                            new WeakHashMap <>()
                    )
            );
this.subscribers=
Collections.synchronizedSet(
Collections.newSetFromMap(
新WeakHashMap()
)
);

当一个元素进入垃圾收集时,我的集合将继续报告它作为收集的一部分。因此,地图似乎在不断扩大

文件说:

当密钥被丢弃时,它的条目将从映射中有效地删除

但实际情况似乎并非如此

是否有一个点是
WeakHashMap
清除碎屑的?

是的,在实际收集垃圾后,钥匙被清除 是的,
WeakHashMap
确实清理了碎屑。
垃圾收集的密钥不再以大小报告。但您必须等待垃圾收集真正发生

似乎您对将对象放入垃圾收集的说法不正确。也许您的对象成为垃圾收集的候选对象,但尚未被收集。尝试调用垃圾收集器并等待它完成。但是请记住,对
System.gc()
的调用只是对JVM的建议,可能会被忽略,具体取决于JVM实现和当前运行时场景

下面是一个完整的示例应用程序。请注意,
Set
报告无论是调用
Set::remove
还是让对象超出范围,
size
都会减少

package com.basilbourque.example;

import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;

public class WeakHashMapExercise {

    public static void main ( String[] args ) {
        WeakHashMapExercise app = new WeakHashMapExercise();
        app.doIt();
    }

    private void doIt ( ) {
        Set < UUID > set =
                Collections.synchronizedSet(
                        Collections.newSetFromMap(
                                new WeakHashMap <>()
                        )
                );

        UUID uuid1 = UUID.fromString( "a8ee1e34-cead-11e8-a8d5-f2801f1b9fd1" );
        UUID uuid2 = UUID.fromString( "39bda2b4-5885-4f56-a900-411a49beebac" );
        UUID uuid3 = UUID.fromString( "0b630385-0452-4b96-9238-20cdce37cf55" );
        UUID uuid4 = UUID.fromString( "98d2bacf-3f7f-4ea0-9c17-c91f6702322c" );

        System.out.println( "Size before adding: " + set.size() );

        set.add( uuid1 );
        set.add( uuid2 );
        set.add( uuid3 );
        set.add( uuid4 );

        System.out.println( "Size after adding 4 items: " + set.size() );  // Expect 4.

        set.remove( uuid3 );

        System.out.println( "Size after removing item # 3: " + set.size() );  // Expect 3.

        uuid2 = null;  // Release that UUID to garbage-collection.

        // That released object may still appear in our `Set` until garbage collection actually executes. 
        System.gc(); // Ask the JVM to run the garbage-collection. Only a suggestion, may be ignored.
        try {
            Thread.sleep( 1_000 );  // Wait a moment, just for the heck of it.
        } catch ( InterruptedException e ) {
            e.printStackTrace();
        }

        System.out.println( "Size after making garbage of item # 2: " + set.size() );  // Expect 2.

        for ( UUID uuid : set ) {
            System.out.println( uuid.toString() );
        }


    }
}
package com.basilbourque.example;
导入java.util.Collections;
导入java.util.Set;
导入java.util.UUID;
导入java.util.WeakHashMap;
公开课威克哈什马普演习{
公共静态void main(字符串[]args){
WeakHashMapExercise app=新建WeakHashMapExercise();
app.doIt();
}
私有无效doIt(){
设置Set=
Collections.synchronizedSet(
Collections.newSetFromMap(
新WeakHashMap()
)
);
UUID uuid1=UUID.fromString(“a8ee1e34-cead-11e8-a8d5-F2801F9FD1”);
UUID uuid2=UUID.fromString(“39bda2b4-5885-4f56-a900-411a49beebac”);
UUID uuid3=UUID.fromString(“0b630385-0452-4b96-9238-20cdce37cf55”);
UUID uuid4=UUID.fromString(“98d2bacf-3f7f-4ea0-9c17-c91f6702322c”);
System.out.println(“添加前的大小:”+set.Size());
set.add(uuid1);
set.add(uuid2);
set.add(uuid3);
set.add(uuid4);
System.out.println(“添加4项后的大小:+set.Size());//预期为4。
设置。移除(uuid3);
System.out.println(“删除项后的大小#3:+set.Size());//预期为3。
uuid2=null;//将该UUID释放到垃圾收集。
//在垃圾回收实际执行之前,释放的对象可能仍会出现在我们的“集合”中。
System.gc();//要求JVM运行垃圾收集。只能忽略一个建议。
试一试{
Thread.sleep(1_000);//稍等片刻,见鬼去吧。
}捕捉(中断异常e){
e、 printStackTrace();
}
System.out.println(“对项#2进行垃圾处理后的大小:+set.Size());//预期为2。
用于(UUID-UUID:set){
System.out.println(uuid.toString());
}
}
}
看这个

添加前的大小:0

添加4项后的大小:4

移除项目后的尺寸#3:3

项目垃圾处理后的尺寸#2:2

在我的例子中,使用基于的from版本,垃圾收集器似乎是根据我的请求激活的。如果我注释掉延迟一秒钟,或者注释掉
System.gc
调用,那么最后报告的大小仍然是
3
,而不是预期的
2

你甚至可以看到这种行为时。注意下面的最后一项是
3
,而上面的是
2

添加前的大小:0

添加4项后的大小:4

移除项目后的尺寸#3:3

项目垃圾产生后的尺寸#2:3

当清除时,它将向引用队列发布一个“事件”。这个过程是,即使GC已经“清除”了您的密钥,仍然有一个对该值的强引用。实际清理发生在以下情况:

  • 垃圾回收器已将事件发布到引用队列(您无法控制何时发生)
  • 您可以调用
    WeakHashMap
    上的任何其他方法,以完成所需的清理
  • 下面是一个例子来说明发生了什么

    public class WeakHashMapInAction {
    
        public static void main(String[] args) {
    
            Key key = new Key();
            KeyMetadata keyMeta = new KeyMetadata("keyMeta");
    
            WeakHashMap<Key, KeyMetadata> map = new WeakHashMap<>();
            map.put(key, keyMeta);
    
            // wrap the key into a weakReference
            WeakReference<Key> keyReference = new WeakReference<>(key);
    
            // force key to be GC-ed
            key = null;
            for (; keyReference.get() != null; ) {
                System.gc();
            }
    
            // at this point keyReference::get returns null,
            // meaning the GC has reclaimed "key";
            // that does NOT mean WeakHashMap removed that entry though
    
            // you can enable this code to see that "not yet collected" is not printed at all
            // since you are giving enough time for the Reference thread to post to that ReferenceQueue
            // LockSupport.parkNanos(10000000);
    
            while (map.size() == 1) {
                // if you run this enough times, you will see this sometimes is printed
                // even if WeakHashMap::size calls "expungeStaleEntries" internally
                // it does not mean that the event to the queue was pushed in time
                // by the Reference thread
                System.out.println("not yet collected");
            }
    
            System.out.println("collected");
    
        }
    
    
        static class Key {
    
        }
    
        @RequiredArgsConstructor
        @Getter
        static class KeyMetadata {
            private final String someInfo;
    
            // Constructor.
            KeyMetadata ( String someInfo ) { this.someInfo = someInfo; }
        }
    
    }
    
    公共类weakhashmapination{
    公共静态void main(字符串[]args){
    键=新键();
    KeyMetadata keyMeta=新的KeyMetadata(“keyMeta”);
    WeakHashMap=新的WeakHashMap();
    map.put(key,keyMeta);
    //将密钥包装成一个weakReference
    WeakReference键引用=新的WeakReference(键);
    //强制加密密钥
    key=null;
    for(;keyReference.get()!=null;){
    gc();
    }
    //此时,keyReference::get返回null,
    //表示GC已回收“密钥”;
    //但这并不意味着WeakHashMap删除了该条目
    //您可以启用此代码以查看“尚未收集”根本没有打印
    //因为您给了引用线程足够的时间发布到该引用队列
    //LockSupport.Parknonas(10000000);
    while(map.size()==1){
    //如果你运行这个足够多的时间,你会看到它有时被打印出来
    //即使WeakHashMap::size在内部调用“expungestalentries”
    //这并不意味着将事件及时推送到队列
    //参考文献