This method is going to release the first item (head) out into the world and head.next will become the new head. The issue here is that head will be pulled off, used, and probably become garbage but the head Node retains its head.next link to the next Node.

This is bad because it means that there will be a chain of references back through all the old Node references even after they’ve been removed from the queue and thrown away. GC will of course take care of all those references since the Nodes are garbage.

But the garbage collectors we use are generational - they periodically sweep through young objects and kill the vast numbers of them that have been short-lived. The ones that survive live to the next generation and eventually if objects are old enough they move to the old generation where they are collected less frequently by a full GC.

The problem in the queue nodes is that if they live in the queue long enough (if the queue is big), then one of them will enter the old gen and at that point the reference from old gen into young gen prevents the whole chain of dead nodes from being reclaimed by young generational collection. This is highly inefficient for the GC.

This problem can of course be solved simply by nulling the Node next reference:

On the original version (the one in the JDK), the second pass through the test is significantly slower and full gc occurred 47 times as opposed to the fixed version which shows a faster second pass and no full gc.

Even worse was a test with the new G1 garbage collector in Java 7. This new collector takes the generational idea and pushes it even further. Unfortunately, that means it’s even more affected by this problem (while in general it is better for many apps):

JDK7 version:Test 1 took: 5456msTest 2 took: 5575ms

With null assignment:Test 1 took: 1698msTest 2 took: 1602ms

It seems this was not the first discovery of this issue as a couple of people were already using modified LBQs with this change. Hopefully this issue will be fixed in Java 7 at least.