GC doesnt remove objects from weakhashmap - java

I hava a test application with one class that stores a map and a button that invokes method of that class:
Map<Object, Object> weakMap = new WeakHashMap<Object, Object>();
The button does this:
public void fillWeakHashMap(int size) {
List<String> createObjects = (List<String>) createObject(size);
for (Object ob : createObjects) {
weakMap.put(ob, ob);
}
}
So the main class with the map lives but objects which I put to it are created in separate method and since it is a WeakHashMap I thought that after exit from the method fillWeakHashMap the keys / objects in map would be cleared by the GC.
I opened the VisualVM->VisualGC and clicked Monitor->PerformGC 10 times and the Old space is almost full meaning that GC isnt clearing those objects. So what am I doing wrong?

The first sentence of WeakHashMap's javadoc says:
Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.
and somewhat further down, it writes:
The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded.
That is, only the keys are weakly referenced, but the values are strongly referenced. In your code, each key is also used a value, therefore strongly referenced, and therefore not garbage collected.

You are keeping a reference for each object in the map like ob is still referring itself that is why nothing is being cleared, since GC cant claimed that those objects are not is use and can be cleaned.

Related

Prevent object garbage collection before other objects

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?
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:
Construct a reference queue for weak references to Bar instances.
ReferenceQueue<Bar> m_refQueue = new ReferenceQueue<>();
Construct each WeakReference with a reference to that queue.
new WeakReference<Bar>( myBar, m_refQueue );
Periodically poll that queue for available collectable instances, and remove them from your collection.
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<>();

Weak Reference maintainability

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?
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.
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.
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.

Automatic removal of entries in WeakHashMap

There is a WeakHashMap instance initialized with, for example, 500 entries. Now, its keys have not been referenced anywhere in the running application for a day or so. Will this map's entries be removed automatically after a certain time gets passed?
My understanding is that if key is not referenced then corresponding entries will be removed from the map.
It will be removed when GC runs if your key is not referenced anywhere: (Reference)
Hash table based implementation of the Map interface, with weak keys.
An entry in a WeakHashMap will automatically be removed when its key
is no longer in ordinary use. More precisely, the presence of a
mapping for a given key will not prevent the key from being discarded
by the garbage collector, that is, made finalizable, finalized, and
then reclaimed. When a key has been discarded its entry is effectively
removed from the map, so this class behaves somewhat differently from
other Map implementations.
The removal time is unknown:
Each key object in a WeakHashMap is stored indirectly as the referent
of a weak reference. Therefore a key will automatically be removed
only after the weak references to it, both inside and outside of the
map, have been cleared by the garbage collector.
But be careful, some object like boxed Integer of small integers like -127-> 127 are cached by JVM so if you use autoboxed Integer key, it will never be removed from the Map.
Well, first we narrow down the question.
QUESTION: We have a WeakHashMap in which we have some entries. Will those entries will be garbage collected if the entries are not being used?
Ref code:
WeakHashMap<Object, Object> wkMap = new WeakHashMap<>()
Object obj1 = new Object();
Object obj2 = new Object();
Objcet obj1Meta = new Object();
Objcet obj2Meta = new Object();
wkMap.put(obj1, obj1Meta);
wkMap.put(obj2, obj2Meta);
First of all, it's not about being used, neither it has any relation with time: it's about whether the reference to the map (wkMap in this case) is in scope; if not, then the entire map is eligible for garbage collection, that's quite straightforward. But if not, then...
One thing we need to check is whether the objects which are weakly referenced by the keys of the map are already garbage collected or not. In this case obj1 and obj2.
If these objects have not been garbage collected, then their corresponding entries will be there in the map. Garbage collector is not going to reclaim. Again straightforward.
Now the tricky case: the objects referenced weakly by obj1, obj2 have been garbage collected. There is no need of their metadata present in the map wkMap. Ideally they should be garbage collected, and eventually they are. But the question is how...
Step by Step
The objects referenced weakly by obj1, obj2 become eligible for garbage collection
The garbage collector collects the objects; at this point, the garbage collector checks whether there are any weak references to the object it's collecting. In our case we have two: keys of two entries in the weak hash map wkMap.
If GC finds some weak references to the object it's collecting, it then checks whether those references have any ReferenceQueue attached to it. If there is any then GC puts the weak reference to that ReferenceQueue. GC is done.
Until now, the entries are there in the map and they are not eligible for garbage collection. And it will be there in the map until someone manually makes the keys set to null. Wait, then who does that? Let's see next:
That manual clean-up is done by WeakHashMap itself. Let us check the size() code inside WeakHashMap:
public int size() {
if (size == 0)
return 0;
expungeStaleEntries();
return size;
}
Concentrate on expungeStaleEntries(); it is the one which removes all the entries from the map which are there in the ReferenceQueue as well, and the entries become eligible for garbage collection (a single reference queue is attached to all the weak references used as a key in the map). Check expungeStaleEntries() code as well.
Now in a nutshell, if from your code you call some method on the WeakHashMap, which internally calls this expungeStaleEntries() method, only then will the entries become eligible for garbage collection.
List of methods which call expungeStaleEntries()
size()
reSize()
isEmpty()
putAll()
etc...
Hope this makes things clear.
Will this map's entries be removed automatically after a certain time gets passed?
It depends on when the Garbage Collector comes. There is no guarantee that it reclaims "garbage" even once a day.
The behavior of the WeakHashMap class depends in part upon the actions of the garbage collector, so several familiar (though not required) Map invariants do not hold for this class. Because the garbage collector may discard keys at any time, a WeakHashMap may behave as though an unknown thread is silently removing entries.
JDK 10 - WeakHashMap
In this code
WeakHashMap<Group,String> map = new WeakHashMap<>();
Group group1 = new Group();
Group group2 = new Group();
map.put(group1,"one");
map.put(group2,"two");
System.out.println(map);
group1 = null;
System.gc();
System.out.println(map);
In the first print statement , you will see that hashmap has two elements and in the second print statement it will have only one element. Because the reference to the first element is now null. So yes all keys whose references point to null will get removed the next time GC runs.
{Group#53e25b76=one, Group#73a8dfcc=two}//First print
{Group#73a8dfcc=two}//Second print

What is the purpose of WeakHashMap when there is HashMap and Concurrent HashMap?

What is the need arises for introducing Weak HashMap when there is already other implementations available.
In short i have two issues :
Why jdk has WeakHashMap when there is HashMap and Concurrent HashMap in java ?
What is the use of it in real life applications ?
EDIT :
Though WeakHashmap key is a weak references but still they refer to
something than on what basis GC discard keys in WeakHashMap.
One Common use of WeakReferences and WeakHashMaps in particular is for
adding properties to objects. Occasionally you want to add some
functionality or data to an object but subclassing and/or composition
are not an option in that case the obvious thing to do would be to
create a hashmap linking the object you want to extend to the property
you want to add. then whenever you need the property you can just look
it up in the map. However, if the objects you are adding properties to
tend to get destroyed and created a lot, you can end up with a lot of
old objects in your map taking up a lot of memory
If you use a WeakHashMap instead the objects will leave your map as
soon as they are no longer used by the rest of your program, which is
the desired behavior.
The key of a WeakHashMap has weak reference. If the key has been garbage collected, then the entry in WeakHashMap object will automatically be deleted. It does not happen in normal HashMap. The entry will not be deleted if the key is garbage collected.
In the example I have taken one HashMap and one WeakHashMap. I will put entry in both the object and then later we will make the reference key as null and then garbage collected. And again check the entry. In the HashMap object entry will be there but in WeakHashMap object there will not be entry present.
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public class WeakHashMapTest {
public static void main(String[] args) {
Map hashMap= new HashMap();
Map weakHashMap = new WeakHashMap();
String keyHashMap = new String("keyHashMap");
String keyWeakHashMap = new String("keyWeakHashMap");
hashMap.put(keyHashMap, "Ankita");
weakHashMap.put(keyWeakHashMap, "Atul");
System.gc();
System.out.println("Before: hash map value:"+hashMap.get("keyHashMap")+" and weak hash map value:"+weakHashMap.get("keyWeakHashMap"));
keyHashMap = null;
keyWeakHashMap = null;
System.gc();
System.out.println("After: hash map value:"+hashMap.get("keyHashMap")+" and weak hash map value:"+weakHashMap.get("keyWeakHashMap"));
}
}
The output would be:
Before: hash map value:Ankita and weak hash map value:Atul
After: hash map value:Ankita and weak hash map value:null
More info:
When would you use a WeakHashMap or a WeakReference?
WeakHashMap vs. HashMap
unlike the normal collections, which hold data until you decide to clear it out, data in WeakHashMap may be removed at any point in time and without notice when the JVM decides it wants the memory back. this makes it suitable for all sorts of caching purposes.
you can read up on weak references (and various other related types) here
First you should understand the purpose of WeakReferences. Once you understand that then reading the WeakedHashMap definition from docs clearly tells its purpose
A hashtable-based Map implementation with weak keys. An entry in a
WeakHashMap will automatically be removed when its key is no longer in
ordinary use. More precisely, the presence of a mapping for a given
key will not prevent the key from being discarded by the garbage
collector, that is, made finalizable, finalized, and then reclaimed.
When a key has been discarded its entry is effectively removed from
the map, so this class behaves somewhat differently from other Map
implementations.
You can use an object as a key without preventing its collection.
From the WeakHashMap documentation:
When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations.
They provide a a way of using an object as a key without creating a strong reference to it. This is good practice where you don't want to hinder JVM's ability to garbage collect the object but yet still want the ability to track some aspect of the object. This makes them ideal for caching or storing metadata about the object.
From the answers, the only cache feature is automatic eviction on GC and it probably won't happen during young gen GC but in tenured old gen GC for the usecase of caching large files that are used for sometime. Is the entry evicted on any GC or only when tenured generation is full?
Besides unpredictable eviction there is no size constraint that can be specified on the map, so any cache library with pre-defined cache size (number of elements and memory size) along with an eviction strategy seems like a better option to me as most caches also include a provision to reduce size/flush more elements when heap usage goes beyond a certain size. These also provide consistency during concurrency.
So, I wonder if there is a good use case to use this besides the risk of memory leaks and missed hits due to bad design.

Java Weak Hash Map - Need to remove entry based on weakness of value, not key

So the Java WeakHashMap lets one create a map whose entries are removed if its keys become weak. But how can I create a Map whose entries are removed when the values in the map become weak? The reason I want to use a map is as a Global Hash Table which keeps track of objects according to their ID's.
ID ---> Object Address
Key ---> Value
(Where ID is a text string)
I want key-value pairs to be removed when the object addresses become weak, not the Strings that point to them. Anyone any thoughts on this?
Such a map is supported, for example, in Guava:
Map<..., ...> m = new MapMaker().weakValues().makeMap();
Why do you want the entry to be garbage collected ? I see two reasons
avoid memory leaks (avoid to keep a weakReference pointing to nothing in your Map)
myMap.get(myKey) should return null if the object was garbage collected.
Solution use a regular HashMap:
Map<String, WeakReference<Object>>
then if 2) is the sole problem just use myMap.get(myKey).get()
If you need to also remove the entries have a look at this post that describes a softHashMap and adapt it to use weakReferences...
The API has the answer:
Implementation note: The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded. Note that a value object may refer indirectly to its key via the WeakHashMap itself; that is, a value object may strongly refer to some other key object whose associated value object, in turn, strongly refers to the key of the first value object. One way to deal with this is to wrap values themselves within WeakReferences before inserting, as in:
m.put(key, new WeakReference(value)), and then unwrapping upon each get.
You can do what WeakHashMap does, but to values instead of keys: wrap your values in WeakReferences, and associate them with a ReferenceQueue held in the map. Whenever the map is accessed, check the ReferenceQueue to see if anything has been added, and if it has, remove its entry from the map. You'll need to make a subclass of WeakReference that holds the key, so you know which entry to remove. You will also need to add a check to methods which query the map (get and containsKey, the iterator methods, etc) to check that a retrieved WeakReference actually contains a value (remember to either ban null values, or use a special sentinel object to represent them).

Categories

Resources