问题
In the code below nameRef.get()
is null , after name = null
and System.gc()
.
import java.lang.ref.WeakReference;
public class Main {
public static void main(String[] args) {
String name = new String("ltt");
WeakReference<String> nameRef = new WeakReference<>(name);
System.out.println(nameRef.get()); // ltt
name = null;
System.gc();
System.out.println(nameRef.get()); // null
}
}
WeakHashMap is based on WeakReference. At last, I think map.size()
will be 0. In fact, it's 1.
import java.util.WeakHashMap;
public class Main2 {
public static void main(String[] args) {
String name = new String("ltt");
WeakHashMap<String, Integer> map = new WeakHashMap<>();
map.put(name, 18);
System.out.println(map.size()); // 1
name = null;
System.gc();
System.out.println(map.size()); // it's 1. why not 0 ?
}
}
When will Java WeakHashMap clean the null
key?
回答1:
The simple answer: you don't know.
Meaning: you have no control when the jvm kicks in and triggers a GC cycle. You have no control when eligible objects are actually collected.
Thus there is no way for you to know when the "state" of that map changes.
And yes, the other part is that calling System.gc() is only a recommendation to the jvm to do garbage collection. In a standard setup you have zero control if jvm follows that request, or ignores it. You would need to use very special JVMs for example to change that.
Typically, gc runs only take place when the jvm considers them necessary. So your map might keep its size at 1 as long as there is no shortage of memory. And as the answer by Holger nicely outlines even the GC running doesn't force the map to update that part.
回答2:
System.gc()
only suggests a garbage collection. There are no guarantees made on when and even if the collection will happen. You are seeing exactly that, you made the suggestion but it was not done yet.
See the documentation:
[...] Calling the
gc
method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects. [...]
Having said that, there is no way for you to know when the entry in the map is gone, except checking it regularly.
回答3:
As pointed out by others, there is no guaranty that System.gc()
performs an actual garbage collection. Neither is a garbage collection cycle guaranteed to actually collect a particular unreachable object.
But in your specific setup, it seems that System.gc()
is sufficient to collect the object, as determined by your WeakReference
example. But clearing a weak reference is not the same as enqueuing the reference, to allow the WeakHashMap
to perform its cleanup. As explained in this answer, the cleanup of the WeakHashMap
relies on the key reference to be enqueued, which will be checked each time you’re invoking a method on it, which is the subsequent map.size()
invocation in your example.
As the documentation of WeakReference specifies:
Suppose that the garbage collector determines at a certain point in time that an object is weakly reachable. At that time it will atomically clear all weak references to that object and all weak references to any other weakly-reachable objects from which that object is reachable through a chain of strong and soft references. At the same time it will declare all of the formerly weakly-reachable objects to be finalizable. At the same time or at some later time it will enqueue those newly-cleared weak references that are registered with reference queues.
Note how clearing happens “at that time” whereas enqueuing will happen “at the same time or at some later time”.
In case of HotSpot JVM/OpenJDK, enqueuing happens asynchronously after the garbage collection and your main thread seems to run too fast, so the key reference has not been enqueued yet.
Inserting a small pause may cause the example program to succeed in typical environments:
import java.util.WeakHashMap;
public class Main2 {
public static void main(String[] args) throws InterruptedException{
String name = new String("ltt");
WeakHashMap<String, Integer> map = new WeakHashMap<>();
map.put(name, 18);
System.out.println(map.size()); // 1
name = null;
System.gc();
Thread.sleep(10);
System.out.println(map.size()); // 0
}
}
Of course, this doesn’t change the fact that this is neat for testing, but not a guaranteed behavior, so you shouldn’t write production code relying on it.
回答4:
Your demo has some problems. In fact, the size is 0 after System.gc(). In you situation is because when print the map's size GC has not finished yet, so the result is 1
来源:https://stackoverflow.com/questions/54959146/when-will-java-weakhashmap-clean-null-key