1

I have a weakly-referenced array of weak references to further objects like so:

public class Foo{
    WeakReference<WeakReference<Bar>[]> cache;
}
public class Bar{
    private final WeakReference<Bar>[] ownerCache;
}

The array itself is weakly referred for reasons I will not go into right now. I want to make sure it is not garbage collected before ANY of the Bar objects reachable from it. In other words, it must exist in memory as long as any Bar object exists that can be reached from it. Then, if no Bar objects exist any more, I am better off if the array is also garbage collected. (Bar objects may or may not be strongly reachable from elsewhere.) I did this by referring to the cache array in a field inside all Bar objects. If the field is sufficient in making the array strongly reachable, it is not garbage collected. However, my code never actually uses that field and I can not make it public. (I get the "unused" warning on it.) I am afraid that the existence of such field is terminated either during compile time or run time, or it could get special treatment from the garbage collector that I am unaware of.

Is this the right solution? Does this solution achieve what I want regardless of the garbage collector or JVM implementation? If not, what would be a better method?

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
Gergely
  • 159
  • 2
  • 14
  • 3
    A heap field is sufficient to keep an object alive if its collection would have an observable effect, whether the field is used or not. This is addressed in [JLS, &12.6.1](https://docs.oracle.com/javase/specs/jls/se13/html/jls-12.html#jls-12.6.1-410): *Note that this sort of optimization is only allowed if references are on the stack, not stored in the heap.* You can add `@SuppressWarnings("unused")` to it. – Holger Feb 28 '20 at 23:35

1 Answers1

2

Here are a couple of ideas.

If you control the Bar class, and each instance is referenced by no more than one array, you could add a reference from a Bar instance to the array. Reachable Bar instances will prevent the array from being collected.

Alternatively, you could:

  1. Construct a reference queue for weak references to Bar instances.

    ReferenceQueue<Bar> m_refQueue = new ReferenceQueue<>();
    
  2. Construct each WeakReference with a reference to that queue.

    new WeakReference<Bar>( myBar, m_refQueue );
    
  3. Periodically poll that queue for available collectable instances, and remove them from your collection.

  4. You could make the collection itself a resizable data structure, avoiding the need to collect it.

    public class Foo {
       final @Nonnull List<WeakReference<Bar>> cache = new ArrayList<>();
    
       // Or you could use an IdentityHashSet from a third-party library.
    }
    

EDIT

As suggested by @Holger below, if an ordered list of references is not needed, your collection of WeakReference can be a java.util.WeakHashMap, used as a set. The keys are weak references; the values can be null. The map is a resizable data structure, so you can simply hold an ordinary reference to the map.

    public class Foo {
       final @Nonnull WeakHashMap<WeakReference<Bar>,Object> cache
          = new WeakHashMap<>();
Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
  • 1
    This was very helpful in opening my mind to other ways I can achieve the same. I switched to using a HashMap with WeakReference values that also store their keys, so I can use reference queue to remove finalized entries from the map. I didn't realize reference queue can be used this way. – Gergely Mar 01 '20 at 07:02
  • 2
    @Gergely you know that there’s [`WeakHashMap`](https://docs.oracle.com/javase/8/docs/api/?java/util/WeakHashMap.html) already doing exactly that? – Holger Mar 02 '20 at 10:38
  • 1
    I know about WeakHashMap, I did consider it, but I would want weakly referred values rather than keys. And using it as a weak Set wouldn't do me any good since I need to somehow index my entries. Basically I would need to be able to answer the question "Is the entry with a given integer id still in memory." very frequently. And I don't have any control over the ids, only knowing that they were generated by a non-salted counter. 0,1,2,3,4... – Gergely Mar 03 '20 at 11:33