When will Java WeakHashMap clean null key? - java

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?

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.

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.

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.

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

Related

Does not storing a newly declared object cause a memory leak?

What I mean to say through the post title is - doing this:
public static void makeNewObjectAndDoTask() {
new SomeClass().doYourTask();
}
I have myself written such code in languages such Java and JavaScript - declaring a new object without storing it in a variable, JUST to call one of its methods. Does this cause memory leaks? ..or does the object get cleared at the end of the method-stack / gets freed by the Java Garbage Collector?
Should I just be doing this instead - for safety?:
public static void makeNewObjectAndDoTask() {
SomeClass obj = new SomeClass().doYourTask();
obj = null;
//System.gc(); // Perhaps also call the collector manually?
}
As the commentors already answered, there is no memory leak in code like
public static void makeNewObjectAndDoTask() {
new SomeClass().doYourTask();
}
at least in itself, assuming that the SomeClass() constructor and the doYourTask() methods don't create memory leaks.
Definitely, the garbage collector will clean up the SomeClass instance at some time in the future.
How does it work?
Instances that are no longer accessible from program code will be garbage collected. Accessibility means being referenced in a variable, field, array element, method argument and so on.
As soon as the new SomeClass().doYourTask(); statement has finished, there is no way to access this individual SomeClass instance any more. So, it fulfills the garbage collection criteria.
The next time the garbage collector runs, it can reclaim the memory occupied by the instance (and its fields, recursively, as long as they aren't referenced elsewhere).
The alternative code
public static void makeNewObjectAndDoTask() {
SomeClass obj = new SomeClass().doYourTask();
obj = null;
}
only delays the garbage collection opportunity, as it stores a reference in obj, thus making the instance accessible for at least a tiny additional period of time, until you assign obj = null;.
Manually calling the garbage collector as in System.gc(); rarely is a good idea. It forces the GC to run (and to spend execution time on cleaning up memory), instead of relying on the JVM's highly optimized GC scheduling strategies. Don't do it unless you have a thorough understanding of the garbage collector, which led you to the conclusion that the GC strategy fails in your case.
We don't want OutOfMemoryErrors, and we don't want excessive time wasted for garbage collection, and the standard GC system does a very good job in both aspects.

Weakly referenced object won't get garbage collected

My concern is about an instance of an object that was once strongly referenced, but after an explicit null assignment to its strong reference and after an explicit System.gc() call, the instance is still reachable via the weak reference. If I understand correctly, when a referred object has only weak references left, the referent is guaranteed to be cleared in the next GC session. What am I missing?
Reference code:
public class References {
public static void main(String[] args) {
Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();
WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);
System.gc();
Example retrievedExample = exampleWeakReference.get();
retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared
strongReferenceWrappedInWeak = null; //eligible for garbage collection
System.gc();
Example retrievedExampleTwo = exampleWeakReference.get(); //should be null
retrievedExampleTwo.printA(); //should throw NPE
}
}
class Example {
private int a;
Example(int a) {
this.a = a;
}
void printA() {
System.out.println(this.a);
}
}
strongReferenceWrappedInWeak = null does not make the Example object instance eligible for garbage collection, because retrievedExample still maintains a strong reference to it.
To fix, add retrievedExample = null;
Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();
WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);
System.gc();
Example retrievedExample = exampleWeakReference.get();
retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared
retrievedExample = null;
strongReferenceWrappedInWeak = null; //now eligible for garbage collection
System.gc();
Example retrievedExampleTwo = exampleWeakReference.get(); //will be null
retrievedExampleTwo.printA(); //will throw NPE
Alternatively, don't create a strong reference with a local variable, just call the method directly off the weak reference. That way you don't accidentally leave a strong reference behind, as you did. *(During the printA() call, the this reference is a strong reference, so object cannot be GC'd during the call)*
Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();
WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);
System.gc(); //does not collect object, since strong reference still exists
exampleWeakReference.get().printA(); //works
strongReferenceWrappedInWeak = null; //eligible for garbage collection
System.gc(); //collects object, since it is now weakly referenced only
exampleWeakReference.get().printA(); //throws NPE
Output (from both)
42
42
Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:**)
Tested on Java 13
Garbage Collection works in mysterious ways.
There have been several implementations of garbage collectors in the Java ecosystem, with very different behaviors.
When garbage collection runs varies by the implementation of the garbage collector, and also may depend on the current condition of the JVM. One collector might run almost continuously, while another might wait until low on memory. (I’m greatly oversimplifying here, to make the point clear.)
Whether all garbage is collected, or just some of it, may also vary by collector implementation and by the state of the JVM.
The call to System.gc is merely a suggestion, not a command. A garbage collector is free to ignore it.
In Java, you should not be making any great effort at managing memory. The modern JVM implementations are far better at that than any single programmer is likely to be. Just be sure to release all references to your objects when done using them. Or use WeakReference/SoftReference. Then trust the JVM and garbage collector to do its job.
In extreme cases (very large memory, or extreme volumes of object churn) you might want to study the behaviors of various garbage collector implementations. And maybe consider alternatives such as Zing from Azul Systems or GraalVM from Oracle. But for most projects, the usual OpenJDK-based JVMs work quite well.

Why enqueuing of PhantomReference takes more GC cycles than WeakReference or SoftReference?

I decided to continue https://stackoverflow.com/a/41998907/2674303 in a separated topic.
Let's consider following example:
public class SimpleGCExample {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> queue=new ReferenceQueue<>();
SimpleGCExample e = new SimpleGCExample();
Reference<Object> pRef=new PhantomReference<>(e, queue),
wRef=new WeakReference<>(e, queue);
e = null;
for(int count=0, collected=0; collected<2; ) {
Reference ref=queue.remove(100);
if(ref==null) {
System.gc();
count++;
}
else {
collected++;
System.out.println((ref==wRef? "weak": "phantom")
+" reference enqueued after "+count+" gc polls");
}
}
}
#Override
protected void finalize() throws Throwable {
System.out.println("finalizing the object in "+Thread.currentThread());
Thread.sleep(100);
System.out.println("done finalizing.");
}
}
Java 11 prints following:
finalizing the object in Thread[Finalizer,8,system]
weak reference enqueued after 1 gc polls
done finalizing.
phantom reference enqueued after 3 gc polls
First 2 rows can change order. Looks like they work in parallel.
Last row sometimes prints 2 gc polls and sometimes 3
So I see that enqueing of PhantomReference takes more GC cycles. How to explain it? Is it mentioned somewhere in documentation(I can't find)?
P.S.
WeakReference java doc:
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
PhantomReference java doc:
Suppose the garbage collector determines at a certain point in time
that an object is phantom reachable. At that time it will atomically
clear all phantom references to that object and all phantom references
to any other phantom-reachable objects from which that object is
reachable. At the same time or at some later time it will enqueue
those newly-cleared phantom references that are registered with
reference queues
Difference is not clear for me
P.S.(we are speaking about object with non-trivial finalize method)
I got answer to my question from #Holger:
He(no sexism but I suppose so) pointed me to the java doc and noticed that PhantomReference contains extra phrase in comparison with Soft and Weak References:
An object is weakly reachable if it is neither strongly nor softly
reachable but can be reached by traversing a weak reference. When the
weak references to a weakly-reachable object are cleared, the object
becomes eligible for finalization.
An object is phantom reachable if
it is neither strongly, softly, nor weakly reachable, it has been
finalized, and some phantom reference refers to it
My next question was about what does it mean it has been finalized I expected that it means that finalize method was finished
To prove it I modified application like this:
public class SimpleGCExample {
static SimpleGCExample object;
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> queue = new ReferenceQueue<>();
SimpleGCExample e = new SimpleGCExample();
Reference<Object> pRef = new PhantomReference<>(e, queue),
wRef = new WeakReference<>(e, queue);
e = null;
for (int count = 0, collected = 0; collected < 2; ) {
Reference ref = queue.remove(100);
if (ref == null) {
System.gc();
count++;
} else {
collected++;
System.out.println((ref == wRef ? "weak" : "phantom")
+ " reference enqueued after " + count + " gc polls");
}
}
}
#Override
protected void finalize() throws Throwable {
System.out.println("finalizing the object in " + Thread.currentThread());
Thread.sleep(10000);
System.out.println("done finalizing.");
object = this;
}
}
I see following output:
weak reference enqueued after 1 gc polls
finalizing the object in Thread[Finalizer,8,system]
done finalizing.
And application hangs. I think it is because for Weak/Soft references GC works in a following way: As soon as GC detected that object is Weak/Soft Reachable it does 2 actions in parallel:
enqueue Weak/Soft into registered ReferenceQueue instance
Run finalize method
So for adding into ReferenceQueue it doesn't matter if object was resurrected or not.
But for PhantomReference actions are different. As soon as GC detected that object is Phantom Reachable it does following actions sequentially:
Run finalize method
Check that object still only phantomReachable(check that object was not resurrected during finalize method execution). And Only if object is GC adds phantom reference into ReferenceQueue
But #Holger said that it has been finalized means that JVM initiated finalize() method invocation and for adding PhantomReference into ReferenceQueue it doesn't matter if it finished or not. But looks like my example shows that it really matter.
Frankly speaking I don't understand the difference according to adding into RefernceQueue for Weak and Soft Reference. What was the idea?
The key point is the definition of “phantom reachable” in the package documentation:
An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
bold emphasis mine
Note that when we remove the finalize() method, the phantom reference gets collected immediately, together with the weak reference.
This is the consequence of JLS §12.6:
For efficiency, an implementation may keep track of classes that do not override the finalize method of class Object, or override it in a trivial way.
…
We encourage implementations to treat such objects as having a finalizer that is not overridden, and to finalize them more efficiently, as described in §12.6.1.
Unfortunately, §12.6.1 does not go into the consequences of “having a finalizer that is not overridden”, but it’s easy to see, that the implementation just treats those objects like being already finalized, never enqueuing them for finalization and hence, being able to reclaim them immediately, which affects the majority of all objects in typical Java applications.
Another point of view is that the necessary steps for ensuring that the finalize() method will eventually get invoked, i.e. the creation and linking of a Finalizer instance, will be omitted for objects with a trivial finalizer. Also, eliminating the creation of purely local objects after Escape Analysis, only works for those objects.
Since there is no behavioral difference between weak references and phantom references for objects without a finalizer, we can say that the presence of finalization, and its possibility to resurrect objects, is the only reason for the existence of phantom references, to be able to perform an object’s cleanup only when it is safe to assume that it can’t get resurrected anymore¹.
​​
¹ Though, before Java 9, this safety was not bullet-proof, as phantom references were not automatically cleared and deep reflection allowed to pervert the whole concept.
PhantomReferences will only be enqueued after any associated finalizer has finished execution. Note a finalizer can resurrect an object (used to good effect by Princeton's former Secure Internet Project).
Exact behaviour beyond the spec is not specified. Here be implementation dependent stuff.
So what seems to be happening? Once an object weakly collectable, it is also finalisable. So the WeakReferences can be enqueued and the objects queued for finalisation in the same stop-the-world event. The finalisation thread(s) is (are) running in parallel with your ReferenceQueue thread (main). Hence you may see the first two lines of your output in either order, always (unless wildly delayed) followed by the third.
Only some time after your finalizer is exited is the PhantomReference enqueueable. Hence the gc count is strictly greater. The code looks like a reasonably fair race. Perhaps changing the millisecond timeouts would change things. Most things GC don't have exact guarantees.

Garbage collection when application ends

As far as I know objects are available to be garbage collected when assigning a null value to the variable :
Object a = new Object;
a = null; //it is now available for garbage collection
or when the object is out of scope due to the method's execution is done:
public void gc(){
Object a = new Object;
} //once gc method is done the object where a is referring to will be available for garbage collection
given with the out of scope isn't also the same when the application just ended?
class Ink{}
public class Main {
Ink k = new Ink();
public void getSomething(){
//method codes here
}
public static void main(String[] args) {
Main n = new Main();
}
}
where I expect 2 objects (Ink object and Main object) should be garbage collected when the application ends.
When the Java application terminates, the JVM typically also terminates in the scope of the OS, so GC at that point is moot. All resources have returned to the OS after as orderly a shutdown of the JVM as the app defined.
You are confusing the event of an object becoming eligible for garbage collection with the actual process of collecting garbage or, more precisely, reclaiming memory.
The garbage collector doesn’t run just because a reference became null or an object went out of scope, that would be a waste of resources. It usually runs because either, memory is low or CPU resources are unused.
Also, the term “garbage collection” is misleading. The actual task for the JVM is to mark all objects being still alive (also known as reachable objects). Everything else is considered reclaimable, aka garbage. Since at the termination of the JVM, the entire memory is reclaimed per se, there is no need to search for reachable references.
That said, it’s helpful to understand, that most thinking about the memory management is useless. E.g. in your code:
public void gc(){
Object a = new Object;
// even here the object might get garbage collected as it is unused in subsequent code
}
the optimizer might remove the entire creation of the object, as it has no observable effect. Then, there will no garbage collection, as the object hasn’t been created in the first place.
See also here.
JVM monitors the GC roots - if an object is not available from a GC root, then it is a candidate for garbage collections. GC root can be
local variables
active java threads
static variables
jni references

Which is a better way to force GC on ThreadLocal instance?

Which is a better way to force GC on ThreadLocal instance?
Simple answer: you can't force the GC in java. Some may posts tricks but all in all you simply can't.
Well, in fact you can. With exit!
The simple and ugly answer:
System.gc();
This does not guarantee garbage collection of a specific object, but it will tell the VM to make an effort to perform general garbage collection.
Specifically, for a ThreadLocal variable, the contained instance of the variable will not be GC'd until the thread dies or the ThreadLocal instance is no longer accessible. So, you will need to kill the associated thread, or you will need to discard your references to the ThreadLocal variable in order for the System.gc() to have any effect.
However, the very fact that you are calling this points to a larger problem in your code. If you want to get rid of an object, simply having no references to it should be sufficient. The VM will come along some time later and clean up your mess.
To repeat: There is no reason that clean code should be explicitly calling GC.
You can't force GC and in many cases when you run in a container even calling System.gc() won't help since the JVM usually set with -XX:+DisableExplicitGC. The JVM will ignore your explicit calls.
The jlibs library has a good utility class for garbage collection. You can force garbage collection using a nifty little trick with WeakReference objects.
RuntimeUtil.gc() from the jlibs:
/**
* This method guarantees that garbage collection is
* done unlike <code>{#link System#gc()}</code>
*/
public static void gc() {
Object obj = new Object();
WeakReference ref = new WeakReference<Object>(obj);
obj = null;
while(ref.get() != null) {
System.gc();
}
}
A ThreadLocal<Object> is just a key. The actual values are stored in eachThread’s threadLocals weak hash map, which maps weak ThreadLocal to strong Object.
Assume that nobody else holds a reference to the Object in a ThreadLocal. Then there are two ways for the value of a ThreadLocal value to be garbage collected. First, there’s the easy way:
The thread finishes (internally setting its threadLocals map to null).
The garbage collector runs, deleting the Thread’s ThreadLocalMap and all unreachable Objects within it.
But if the thread is still running, then this sequence must happen before your Object can be garbage collected:
You lose all references to the ThreadLocal.
The garbage collector deletes the ThreadLocal.
You set() some number of other ThreadLocals within that thread, and the ThreadLocalMap removes the stale entry from the map. The number needed is nondeterministic because unlike a standard WeakHashMap, the ThreadLocalMap only expunges a few stale entries at a time, and only on set(), not get().
The garbage collector finally deletes the Object.
To mitigate the problem of slow garbage collection, you can call ThreadLocal.remove() from each Thread if that thread doesn’t need the object anymore.

Categories