简体   繁体   中英

Avoiding objects garbage collection

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 Item s 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).

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; eg 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 used 1 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM