Weak references cleared atomically with placement on reference queue? - java

Does the Java GC clear a weak reference atomically with placing the weak reference on the ReferenceQueue assigned to that reference? In other words, if a call to WeakReference.get() returns null, will the WeakReference be on the queue?

Lets dissect Javadoc for WeakReference:
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.
It looks like the steps are:
Determine if object is weakly reachable.
Clear that object from its WeakReference. WeakReference.get
will return null.
Mark that object finalizable.
Right after or at some other time do enqueue the
WeakReference into the queue (if WeakReference was created with
a queue).
This means that even if WeakReference.get() returns null it is not guarantee that WeakReference.enqueue will be true or ReferenceQueue.poll wont return null.
See https://community.oracle.com/blogs/enicholas/2006/05/04/understanding-weak-references for more.
Reference queues
Once a WeakReference starts returning null, the object it pointed to
has become garbage and the WeakReference object is pretty much
useless. This generally means that some sort of cleanup is
required;WeakHashMap, for example, has to remove such defunct entries
to avoid holding onto an ever-increasing number of deadWeakReferences.
The ReferenceQueue class makes it easy to keep track of dead
references. If you pass a ReferenceQueueinto a weak reference's
constructor, the reference object will be automatically inserted into
the reference queue when the object to which it pointed becomes
garbage. You can then, at some regular interval, process the
ReferenceQueue and perform whatever cleanup is needed for dead
references.
Sample code showing it:
public static class A {
}
public static void main(String[] args) throws Exception{
A a = new A();
ReferenceQueue<A> rq = new ReferenceQueue<A>();
WeakReference<A> aref = new WeakReference<A>(a, rq);
a = null;
//aref.get() should be a, aref.isEnqueued() should return false, rq.poll() should return null
System.out.println( "0: " + aref + " : " + aref.get() + " : " + aref.isEnqueued() + " " + rq.poll() );
Thread.sleep(1000);
System.out.println("Running GC.");
Runtime.getRuntime().gc(); //let GC clear aref
System.out.println("GC ran.");
//aref.get() should be null, aref.isEnqueued() should return false, rq.poll() should return null
System.out.println( "1: " + aref + " : " + aref.get() + " " + aref.isEnqueued() + " " + rq.poll() );
//give some time for GC to enqueue aref
Thread.sleep(1000);
//ref.get() should be null, aref.isEnqueued() should return true, rq.poll() should return aref
System.out.println( "2: " + aref + " : " + aref.get() + " " + aref.isEnqueued() + " " + rq.poll() );
}

Does the Java GC clear a weak reference atomically with placing the weak reference on the ReferenceQueue assigned to that reference?
No. It is queued 'at the same time or some later time'. Nothing atomic about that specification.
In other words, if a call to WeakReference.get() returns null, will the WeakReference be on the queue?
Not necessarily. Note that this isn't really 'in other words' at all: it isn't the same question.

Related

How to find if an object is referencing another at runtime

is it possible to check at runtime if an object has a direct or indirect reference to another object?
(I know I can use VisualVm or similar to analyze the HeapDump, but i'd like to automate it at runtime)
I'm working with WeakHashMaps, doing something like this:
public class MyClass {
// the Runnable will be eventually removed if the key is collected by the GC
private static final WeakHashMap<Object, Runnable> map = new WeakHashMap<>();
public static void main(String[] args) {
MyClass a = new MyClass(2);
MyClass b = new MyClass(20);
a = null;// no more Strong references to a
b = null;// no more Strong references to b
System.gc();
for (Runnable r : map.values()) {
r.run();
}
// will print (20), becouse using format() in the lambda cause a Strong
// reference to MyClass (b) and thus the WeakHashMap will never remove b
}
public MyClass(int i) {
if (i < 10) {
map.put(this, () -> {
System.out.println(i);
});
} else {
map.put(this, () -> {
// this is subtle, but calling format() create a strong reference
// between the Runnable and MyClass
System.out.println(format(i));
});
}
}
private String format(Integer i) {
return "(" + i + ")";
}
}
in the code, the two instance of MyClass will add themselves (as key) and a runnable (as value) to the WeakHashMap.
In the first instance (a), the Runnable simply call System.out.println() and when the instance a is no more referenced (a = null) the entry will be removed from the map.
In the second instance (b), the Runnable also call format() an instance function of MyClass. This create a strong reference to b and adding the Runnable to the map will result in a lock condition, where the value is an indirect strong reference to the key preventing the collection by the garbage collector.
Now I know to avoid these conditions (for instance, using a Weakreference inside the lambda), but this is really easy to miss in a real scenario, and will cause a memory leak.
So, prior to adding the pair to the map I'd like to check if the value is somehow referencing the key, and throw an exception if so.
This would be a "debug" task, and will be disabled in production, so I don't care if it is slow or an hack.
--- update ---
I'm trying to deal with WeakListeners, and to avoid them to be immediately collected if not referenced.
So i register them as notifier.addWeakListener(holder, e -> { ... });
and this will add the listener to a WeakHashMap preventing the listener to be collected until holder live.
But any reference to the holder in the listener will create a lock :(
Is there a better way?
The Reflection API gives you access to all fields of a run-time object (and its runtime type, and possibly the Class object). In theory, you could traverse through the tree of you instance's fields (and static fields on the class), the fields' fields etc.
While this is possible, I doubt it would be feasible. You write you don't care about performance, but it may even be too slow for development runs. Which brings us to the Rule 1 of implementing your own cache: Don't do it.
There is already a builtin feature for associations which are automatically cleaned up, ordinary instance fields. I.e
public class MyClass {
public static void main(String[] args) {
MyClass a = new MyClass(2);
MyClass b = new MyClass(20);
WeakReference<MyClass> aRef = new WeakReference<>(a), bRef = new WeakReference<>(b);
a = null;// no more Strong references to a
b = null;// no more Strong references to b
System.gc();
if(aRef.get() == null) System.out.println("a collected");
if(bRef.get() == null) System.out.println("b collected");
}
Runnable r;
public MyClass(int i) {
if (i < 10) {
r = () -> System.out.println(i);
} else {
r = () -> {
// reference from Runnable to MyClass is no problem
System.out.println(format(i));
};
}
}
private String format(Integer i) {
return "(" + i + ")";
}
}
You can put these associated objects into a weak hashmap as keys, to allow them to get garbage collected, which, of course, will only happen when the particular MyClass instance, which still holds a strong reference to it, gets garbage collected:
public class MyClass {
public static void main(String[] args) {
MyClass a = new MyClass(2);
MyClass b = new MyClass(20);
for(Runnable r: REGISTERED) r.run();
System.out.println("cleaning up");
a = null;// no more Strong references to a
b = null;// no more Strong references to b
System.gc();
// empty on common JRE implementations
for(Runnable r: REGISTERED) r.run();
}
static Set<Runnable> REGISTERED = Collections.newSetFromMap(new WeakHashMap<>());
Runnable r;
public MyClass(int i) {
r = i < 10?
() -> System.out.println(i):
() -> {
// reference from Runnable to MyClass is no problem
System.out.println(format(i));
};
REGISTERED.add(r);
}
private String format(Integer i) {
return "(" + i + ")";
}
}
But note that what works smoothly in this simple test setup is nothing you should rely on, especially as you mentioned weak listeners.
In production environments, the garbage collector runs when there are memory needs, which is not connected to application logic, i.e. whether particular actions implemented as listeners should be executed or not. One possible scenario would be that there is always enough memory, so the garbage collector never runs and obsolete listeners keep being executed forever.
But you may encounter problems into the other direction too. Your question suggests that it might be possible to write your listeners (Runnable in the example) in a way that they don’t contain references to the instance whose life time ought to determine the listener’s life time (the MyClass instance). This raises the question, in which way the life times of these objects are connected at all. You have to keep strong references to these key objects, for the sake of keeping these listeners alive, which is error prone too.

Trying to understand the accepted answer - Thread Safe Map of Queues

Thread safe Map of Queues
The above question has a design implementation VERY similar to my own. I understand why it's not thread safe, but the accepted "pattern" is throwing me for a loop. I don't quite get how to implement it or what it does in relationship to the question.
boolean send = false;
System.out.println("MailCenter Size Start: " + mailBox.keySet().size());
if (parsedInput.size() == 3) {
for (String s : writers.keySet()) {
if (!s.equals(parsedInput.get(1))) {
send = true;
}
}
if (send) {
mailMessage.add(noAnsiName + ": " + parsedInput.get(2));
mailBox.putIfAbsent(parsedInput.get(1), mailMessage);
System.out.println("Current mail message is: " + mailMessage.peek());
out.println("SERVER You have sent mail to " + parsedInput.get(1) + ".");
}
System.out.println("MailCenter Size Middle: " + mailBox.keySet().size());
} else {
int loop = 0;
for (Map.Entry entry : mailBox.entrySet()) {
System.out.println(entry.getKey() + ":\t" + entry.getValue());
System.out.println("*** LOOP STATUS *** " + loop);
loop++;
}
}
I don't quite get how to implement it or what it does in relationship
to the question.
The question shows that a ConcurrentHashMap is being used. However, the variable map is being declared as a Map, so the method putIfAbsent() referenced by the accepted answer is not visible.
Map<String, ConcurrentLinkedQueue<String>> map = new ConcurrentHashMap<>();
So, in order for the answer to work, the above must be changed to declare map as a ConcurrentMap.
Now, that's a good step toward thread safety. However, just because we're using a concurrent map implementation doesn't mean a get() and subsequent put() on the map are an atomic unit of work. In other words, another thread can still change the state of the map after the current thread calls get(), but before it calls put().
The answer recommends putIfAbsent() because it ensures atomicity by wrapping the equivalent of a containsKey() call and a put() call in a synchronized block. If you also then use the return value correctly, you will have solid concurrent behavior.

Java WeakHashMap Class

I wanted to test Java WeakHashMap Class functionality and for that matter I wrote the following test:
public class WeakHashMapTest {
public static void main(String args[]) {
Map<String, Object> weakMap = new WeakHashMap<>();
String x = new String("x");
String x1 = new String("x1");
String x2 = new String("x2");
weakMap.put(x, x);
weakMap.put(x1, x1);
weakMap.put(x2, x2);
System.out.println("Map size :" + weakMap.size());
// force all keys be eligible
x=x1=x2=null;
// call garbage collector
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Map size :" + weakMap.size());
System.out.println("Map :" + weakMap);
}
}
After running the class WeakMapTest I was unpleasantly surprised to get the following output:
map before gc: {x=x, x1=x1, x2=x2}
map after gc: {x=x, x1=x1, x2=x2}
when I expected that the map will be empty.
That is, the garbage collector didn't do its work. But why?
The WeakHashMap will have its keys reclaimed by the garbage collector when they are no longer strongly reachable.
Implementation note: The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded.
However, because you used the key itself as the value, the value is still strongly reachable, which means that the garbage collector can't reclaim the keys.
However, if you use other objects as values, then the only references to the keys will be the keys themselves.
weakMap.put(x, new Object());
weakMap.put(x1, new Object());
weakMap.put(x2, new Object());
Then, after clearing the variables and calling the garbage collector as you've done already, I get the output:
Map size :3
Map size :0
Map :{}
Even if the call to System.gc() doesn't guarantee the garbage collector runs, it looks like it does run here.
System.gc() is effectively a suggestion to run the garbage collector. There is no guaranteed way to force the garbage collector to run.

WeakReference to String and String constants

I have come across this example from wikipedia regarding weak reference:
import java.lang.ref.WeakReference;
public class ReferenceTest {
public static void main(String[] args) throws InterruptedException {
WeakReference r = new WeakReference(new String("I'm here"));
WeakReference sr = new WeakReference("I'm here");
System.out.println("before gc: r=" + r.get() + ", static=" + sr.get());
System.gc();
Thread.sleep(100);
// only r.get() becomes null
System.out.println("after gc: r=" + r.get() + ", static=" + sr.get());
}
}
I don't understand in this scenario why only r.get() returns null but not the sr.get(). Can someone let me know the reason?
Many thanks.
the literal "I'm here" is a compile time constant string and as such gets placed in the constant string pool, which (up until java 7) was never garbage collected. that means sr points to an object that will never be garbage collected.
r, on the other hand, points to a copy of that string, which is not in any const pool and so is eligible for collection.
see the documentation for String.intern() for some more details on this string pool
If this reference object has been cleared, either by the program or by the garbage collector, then this method returns null.
very well explained here

Weak reference not getting garbage collected?

I was studying about Weak references. And I understood that all weak references WILL be garbage collected before OutOfMemoryError occurs. I had a simple test something like this (I know catching OOME is not good but just a test) :
Integer weakInt = new Integer(10);
WeakReference<Integer> weakReference = new WeakReference<Integer>(weakInt);
try {
while (weakReference != null) {
String[] generateOutOfMemoryStr = new String[999999999];
}
}
catch (OutOfMemoryError oome) {
System.out.println(weakReference.get());
}
I expected null to be printed because the weak reference SHOULD have been collected but I always get an output of 10.
Please let me know where I am going wrong. May be I understood the concept of weak references wrong?
weakReference itself won't become null... how could it? However, its target can become null.
I suspect you mean:
while (weakReference.get() != null) {
Moreover, I suspect that unless you set weakInt itself to null, that local variable will prevent the Integer object from being garbage collected.
Moreover, I suspect you'll find your loop still won't end - because you're quite possibly asking for more memory than can be allocated even if the Integer is garbage collected.
Here's a program which demonstrates it working, at least on my box:
import java.lang.ref.*;
public class Test {
public static void main(String[] args) {
Integer weakInt = new Integer(10);
WeakReference<Integer> weakReference = new WeakReference<Integer>(weakInt);
weakInt = null;
while (weakReference.get() != null) {
System.out.println("Looping...");
String[] generateOutOfMemoryStr = new String[999999];
}
System.out.println("Weak reference collected");
}
}

Categories