Question

Disclaimer: Please no advice concerning premature optimization. I'm just curious.

Imagine I want to make sure that some objects referenced by a field can be garbage collected ASAP. I'm using a home-made singly linked list like this

class BigData {
    byte[] someBigArray;
    BigData next;
}

private BigData bigData;

and iterate like this

while (bigData != null) {
    process(bigData);
    bigData = bigData.next;
}

Is the JIT free to change it like follows?

BigData tmp = bigData;
while (tmp != null) {
    process(tmp);
    tmp = tmp.next;
}
bigData = null;

Assume there's no other reference to any instance of BigData. Assume process is a simple inlineable method not accessing the field bigData. Both snippets are equivalent (assuming no exception gets thrown in between), the only difference is that the second moves the field access from the loop to the outside.

Disclaimer repeated: Please no advice concerning premature optimization. I'm just curious.


To answer the comment "Even if the change you 'want' is what the JIT does, why would that make the GC collect them faster/sooner?": If the JIT does the change, then that all the big objects can be only collected after the loop. If it doesn't, then each time the loop advances one more object becomes eligible for GC.

Addendum:

Actually, whenever the JIT is free to do the above transform, it can do this one instead:

BigData tmp = bigData;
bigData = null;
while (tmp != null) {
    process(tmp);
    tmp = tmp.next;
}

I can't see any disadvantage here and it makes all objects collectible as soon as in the original code.

Was it helpful?

Solution

Well, the optimizer can optimize

while (bigData != null) {
    process(bigData);
    bigData = bigData.next;
}

into

BigData tmp = bigData;
while (tmp != null) {
    process(tmp);
    tmp = tmp.next;
}
bigData = null;

if the bigData field is not volatile and the process method has no side-effects that forbid this optimization.

But in practice, the code transformation, if any, will look completely different. It’s typical that optimizers do loop unrolling, creating a code block performing a certain number of iterations and do the field storage operation before taking a backwards jump. So there are certain “savepoints” where the garbage collector can jump in. But if the process method contains code that accesses the bigData field or might allocate memory, etc, the field storage will be performed before every invocation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top