3

I was reading up on weak references in java and sounds simple enough, if an object only has weak references on it, then it can be collected by the garbage collector. Except what happens if your reference becomes dead before you use the value?

Example:

Suppose I have a weak hashmap with the keys {1,2,3,4,5}, all with values of 1. Now suppose you have a random number generator for numbers in [1:10]. Now every time the number is gotten, it checks if it a key in the map and then gives a temporary strong reference to the key. So with this setup, you'll have some keys having strong references and thus stay in memory, but you also have the probability that some keys will become dead before being chosen.

If my intuition for weak hashmaps is correct, does that mean that the map will at some point be altered from its original state?

Fancypants753
  • 429
  • 1
  • 6
  • 19

3 Answers3

4

Trying to use Integer objects as keys for a WeakHashMap is likely to result in some strange behavior. To start with, the javadoc for WeakHashMap has the following note:

This class is intended primarily for use with key objects whose equals methods test for object identity using the == operator. Once such a key is discarded it can never be recreated, so it is impossible to do a lookup of that key in a WeakHashMap at some later time and be surprised that its entry has been removed. This class will work perfectly well with key objects whose equals methods are not based upon object identity, such as String instances. With such recreatable key objects, however, the automatic removal of WeakHashMap entries whose keys have been discarded may prove to be confusing.

Consider the following code:

    WeakHashMap<Integer, String> map = new WeakHashMap<>();
    Integer k = Integer.valueOf(9001);
    map.put(k, "OVER 9000!?");

    while (true)
    {
        System.out.println(map.get(k));
        Thread.sleep(100);
        k = Integer.valueOf(9001);
        System.gc();
    }

The loop will start by printing "OVER 9000!?", but after the first loop, the original key has been discarded (even if there's now a reference to a key that is equals to it). As a result, if that key object gets garbage collected, the entry will be removed from the map and the loop will begin printing "null" instead. Since we call System.gc(); after discarding the key, it's likely that this happens after a single loop.

That's not the end of the issues with using Integer as a WeakHashMap key, though. If you change the value 9001 above to 1, you'll find that the behavior changes! (Probably? This may be implementation-dependent.) Now, the entry never gets removed from the map. This is because of the integer cache--Integer.valueOf(1) always returns the same Integer instance, but Integer.valueOf(9001) creates a new Integer instance each time.

This second issue is specific to Integer, but the first actually applies to any scheme where you try to use keys where equals is not based on ==. And if equals is based on ==, then your question doesn't really apply--if you don't have a strong reference to the key anymore, it doesn't matter whether the value gets removed from the map because you no longer have a way to get to it--you can't recreate a key that uses identity-based equality.

VeeArr
  • 6,039
  • 3
  • 24
  • 45
  • point of the post was not supposed to deal with what you are answering. I wanted to know about whether the inability to acquire a key in a certain time frame would result in the key being removed before I had the chance to use it. – Fancypants753 Aug 16 '18 at 23:14
  • The last paragraph discusses this. If you're able to acquire the key in a consistent way, then something must be holding a strong reference to it already and it is a moot question (as long as your keys use identity-based equality). – VeeArr Aug 16 '18 at 23:27
  • So is weak references for situations where you give someone access to a key (by giving them a stack allocated strong reference to it) and when the stack closes (they are done with it) then the lack of a strong reference makes collecting it fair game? – Fancypants753 Aug 16 '18 at 23:31
  • Strong references don't only originate from the stack. They can also be on the heap, for example. The rest of what you've said is generally correct, though--I've generally seen `WeakHashMap` used to cache meta-data or calculated data about the objects used as its keys. When those objects can no longer be reached (because the code using them has finished, for instance), that metadata is no longer needed and can be automatically discarded at the same time those objects get garbage collected. – VeeArr Aug 16 '18 at 23:44
1

This answer already addresses issues stemming from the use of types with value-based equality in a construct whose behavior depends on the identity of objects, like the reachability.

In short, when you are able to construct new objects with the same equality as the weakly reachable keys, it’s possible to detect the sudden removal of the keys.

However, you can also turn weakly reachable objects back to the strongly reachable state, e.g. by calling the get() method of a WeakReference or when iterating over the mappings of a WeakHashMap.

WeakHashMap<Object, Boolean> map = new WeakHashMap<>();
Object key = new Object();
map.put(key, true);
WeakReference<Object> ref = new WeakReference<>(key);
key = null;
// now, the key object is only weakly reachable
key = ref.get();
// now, the key object might be strongly reachable again
// in that case, this statement will print true
System.out.println(map.get(key));

The construction of an object with a distinct identity and no overridden equals method via new Object() ensures that no other reference to the same object nor an equal object can exist. At one point of this code, the object is only weakly reachable, but then, it is made strongly reachable, with a very high likelihood.

It is possible that a garbage collection happens between these points and since all weak references to an object are cleared atomically, you can detect this situation by getting a null reference from get(). The likelihood for a garbage collection to happen right at this point is very low, though. That’s why the linked answer uses calls to System.gc() in-between, to raise the likelihood of the weak references to get cleared.

This is rather a contrived example, but helps addressing you question, “…does that mean that the map will at some point be altered from its original state?”.

If you use equal keys with a different identity or keys which are weakly reachable for some time, the map may get altered at some time, but there is no guaranty that this ever happens. This depends on when the garbage collector will run and actually discover the weak reachability of some objects. But typically, JVMs try to prevent garbage collection until there’s really a demand for it. So an application may run quiet a while without garbage collection at all. Further, if you do poll a mapping regularly, it may even happen, that the gc runs right at that point of time when the key is strongly reachable during the lookup.

Holger
  • 285,553
  • 42
  • 434
  • 765
0

The purpose of WeakReference is to help with memory management. As you wrote, "if the object is not used normally" (there is no strong reference, in fact direct variable holding it), "then you don't need it any more" (it can be garbage collected). In case of weak hash map it aplies to the key, so you typically use it for caching of temporary associated data.

From that being said, it doesnt make sense to only put something into weak hash map without continue using the key as strong reference, because the collector can collect it immediately before you access it.

It may not (even System.gc() doesn't force the GC to run), but you cannot rely on it.

Ondřej Fischer
  • 411
  • 1
  • 7
  • 16