1

TLDR: How can I force the JVM not to garbage collect my objects, even if I don't want to use them in any meaningful way?

Longer story: I have some Items which are loaded from a permanent storage and held as weak references in a cache. The weak reference usage means that, unless someone is actually using a specific Item, there are no strong references to them and unused ones are eventually garbage collected. This is all desired behaviour and works just fine. Additionally, sometimes it is necessary to propagate changes of an Item into the permanent storage. This is done asynchronously in a dedicated writer thread. And here comes the problem, because I obviously cannot allow the Item to be garbage collected before the update is finished. The solution I currently have is to include a strong reference to the Item inside the update object (the Item is never actually used during the update process, just held).

public class Item {
  public final String name;
  public String value;
}
public class PendingUpdate {
  public final Item strongRef; // not actually necessary, just to avoid GC
  public final String name;
  public final String newValue;
}

But after some thinking and digging I found this paragraph in JavaSE specs (12.6.1):

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable. For example, a Java compiler or code generator may choose to set a variable or parameter that will no longer be used to null to cause the storage for such an object to be potentially reclaimable sooner.

Which, if I understand it correctly, means that java can just decide that the Item is garbage anyway. One solution would be to do some unnecessary operation on the Item like item.hashCode(); at the end of the storage update code. But I expect that a JVM might be smart enough to remove such unnecessary code anyway and I cannot think of any reasonable solution that a sufficiently smart JVM wouldn't be able to release sooner than needed.

public void performStorageUpdate(PendingUpdate update) {
  final Transaction transaction = this.getDataManager().beginTransaction();
  try {
    // ... some permanent storage update code
  } catch (final Throwable t) {
    transaction.abort();
  }
  transaction.commit();
  // The Item should never be garbage collected before this point
  update.item.hashCode(); // Trying to avoid GC of the item, is probably not enough
}

Has anyone encounter a similar problem with weak references? Are there some language guarantees that I can use to avoid GC for such objects? (Ideally causing as small performance hit as possible.) Or am I overthinking it and the specification paragraph mean something different?

Edit: Why I cannot allow the Item to be garbage collected before the storage update finishes: Problematic event sequence:

  1. Item is loaded into cache and is used (held as a strong reference)
  2. An update to the item is enqueued
  3. Strong reference to the Item is dropped and there are no other strong references to the item (besides the one in the PendingUpdate, but as I explained, I think that that one can be optimized away by JVM).
  4. Item is garbage collected
  5. Item is requested again and is loaded from the permanent storage and a new strong reference to it is created
  6. Update to the storage is performed Result state: There are inconsistent data inside the cache and the permanent storage. Therefore, I need to held the strong reference to the Item until the storage update finishes, but I just need to hold it I don't actually need to do anything with it (so JVM is probably free to think that it is safe to get rid off).
1
  • 1
    Why does your PendingUpdate have redundant name and newValue fields? When it reads these values from the strongRef instead, this reference is actually used. But a cleaner solution would be to stop the loading from the permanent storage from being ignorant towards pending updates. Commented Aug 22, 2022 at 19:11

1 Answer 1

2

TL;DR How can I force the JVM not to garbage collect my objects, even if I don't want to use them in any meaningful way?

Make them strongly reachable; e.g. by adding them to a strongly reachable data structure. If objects are strongly reachable then the garbage collector won't break weak references to them.

When you finish have finished the processing where the objects need to remain in the cache you can clear the data structure to break the above strong references. The next GC run then will be able to break the weak references.


Which, if I understand it correctly, means that java can just decide that the Item is garbage anyway.

That's not what it means.

What it really means that the infrastructure may be able to determine that an object is effectively unreachable, even though there is still a reference to it in a variable. For example:

public void example() {
    int[] big = new int[1000000];
    // long computation that doesn't use 'big'
}

If the compiler / runtime can determine that the object that big refers to cannot be used1 during the long computation, it is permitted to garbage collect it ... during the long computation.

But here's the thing. It can only do this if the object cannot be used. And if it cannot be used, there is no reason not to garbage collect it.

1 - ... without traversing a reference object.


For what it is worth, the definition of strongly reachable isn't just that there is a reference in a local variable. The definition (in the javadocs) is:

"An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it."

It doesn't specify how the object can be reached by the thread. Or how the runtime could / might deduce that no thread can reach it.

But the implication is clear that if threads can only access the object via a reference object, then it is not strongly reachable.

Ergo ... make the object strongly reachable.

Sign up to request clarification or add additional context in comments.

8 Comments

That is basically what I am doing in the PendingUpdate class, right? If not, could you be please more specific? And yes, strongly reachable objects are, of course, not collected. But, If I understand the specs I referred to correctly, a strong reference to the data won't solve anything unless actually needed for something.
Well ... if they are not actually needed for anything ... you don't need to prevent them from being garbage collected. But the point is that if you do want to prevent them be garbage collected for any reason, making them strongly reachable is the best way to do it.
making them strongly reachable is the best way to do it, again, yes, but the referred specs say JVM can optimize a strongly reachable object away anyway. If it doesn't, could you please explain way?
@fonadius The spec says that it "may choose to set a variable or parameter that will no longer be used to null". It never sets a field of an object (and public final Item strongRef; is a field in an object) to null because no optimizer can know where that field might be used again.
@ThomasKläger it never sets a field to null, however, the optimizer could transform the performStorageUpdate method in a way that it reads the constant fields of the PendingUpdate object right at the beginning of the method. In that case, this method never touches the PendingUpdate object again and if there is no other reference, this also provenly will never touch the Item object again through the strongRef field. In this case, both, the PendingUpdate object and the Item object are eligible to garbage collection together, even when the PendingUpdate object is still in use.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.