All!
I found strange code in LinkedBlockingQueue:
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
Who can explain why do we need local variable h? How can it help for GC?
The LinkedBlockingQueue is an optionally-bounded blocking queue based on linked nodes. It means that the LinkedBlockingQueue can be bounded, if its capacity is given, else the LinkedBlockingQueue will be unbounded. The capacity can be given as a parameter to the constructor of LinkedBlockingQueue.
The LinkedBlockingQueue keeps the elements internally in a linked structure (linked nodes).
If you look at the jsr166 src then you will find the offending commit, scroll down to v1.51
This shows the answer is in this bug report
The full discussion can be found in a jsr.166 mailing list thread
The "helping GC" bit is about avoiding things bleeding into tenured.
Maybe a bit late, but the current explanation is completely unsatisfactory to me and I think I've got a more sensible explanation.
First of all every java GC does some kind of tracing from a root set one way or another. This means that if the old head is collected we won't read the next
variable anyhow - there's no reason to do so. Hence IF head is collected in the next iteration it doesn't matter.
The IF in the above sentence is the important part here. The difference between setting next to something different doesn't matter for collecting head itself, but may make a difference for other objects.
Let's assume a simple generational GC: If head is in the young set, it will be collected in the next GC anyhow. But if it's in the old set it will only be collected when we do a full GC which happens rarely.
So what happens if head is in the old set and we do a young GC? In this case the JVM assumes that every object in the old heap is still alive and adds every reference from old to young objects to the root set for the young GC. And that's exactly what the assignment avoids here: Writing into the old heap is generally protected with a write barrier or something so that the JVM can catch such assignments and handle them correctly - in our case it removes the object next
pointed to from the root set which does have consequences.
Short example:
Assume we have 1 (old) -> 2 (young) -> 3 (xx)
. If we remove 1 and 2 now from our list, we may expect that both elements would be collected by the next GC. But if only a young GC occurs and we have NOT removed the next
pointer in old, both elements 1 and 2 won't be collected. Contrary to this if we have removed the pointer in 1, 2 will be collected by the young GC..
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With