I'm using Guava's Cache<Key, Value>. Whenever Key is no more strongly reachable, the cache entry should be garbage collected (someday...). Using CacheBuilder.weakKeys() would do exactly that, if there weren't a reference from Value back to Key.
I could make this reference weak, but this could anytime make my Value quite invalid. I could handle it, but I'd prefer not to.
I could use weakValues(), but this could lead to very early evictions, as my values are only referenced for a short time.
Maybe I could use softValues(), but SoftReferences are quite broken.
Probably I'm getting something wrongly.... what is the right solution?
Update
What I need could be achieved simply by putting a reference to Value into each Key, but this is not possible as Key is not under my control. If it was, then I'd need no cache, no weak references, nothing.
This way, each Key would keep its corresponding Value reachable, which is fine1. Also each Value would keep its Key reachable, but this is no problem as there're no long existing references to Value.
1 Some expiration would be better but it's not necessary.
Unfortunately, this is unsolvable without ephemerons.
The pointer from Value -> Key doesn't matter as long as nothing else is holding on to Value.
When the Cache dumps Key, it will be collected.
If you have System->Cache->Key<-Value, when Cache drops key you get System->Cache Key<-Value. The link from Key back up to System (the memory root for this example) is broken, and Key will be recovered.
If you really want weakKeys, then having a weak reference from the value to the key is the right thing to do.
If that doesn't feel right to you, then please provide more info about what you're trying to accomplish.
Do you think it might be possible to create a copy of key, and use that as the key in the map? I am thinking you might have something like
Value v = SomeLibrary.giveMeSomething();
String k = v.getName();
String k1 = new String(k);
cache.put(k1,v);
This will work b/c k.equals(k1) and k != k1. Hopefully you can create a copy or clone of the type used for Key (which probably isn't String in your case).
However, this changes the lifecycle of the key -- since it is no longer the one in Value. If you have control over the lifecycle of the particular object you've put in the map, then you're OK.
Do you think that might work?
Related
I've run across a piece of code that I am convinced will cause an inadvertent memory leak:
Object user = getUser("Bob");
Map<String, WeakReference<Object>> map = new HashMap<>();
map.put("Bob", new WeakReference( user ) );
The purpose of this map is to cache the Objects and to have them automatically cleared from the map by the GC when they are no longer strongly referenced.
However, the way I see it is if the key isn't a weak reference as well, then once the Object is GC'ed, there will still be an entry in the hash map with the key pointing to null. Hence the map will still contain the same number of rows, just all pointing to null values.
So in the above example, once all strong references to user are released, and the GC destroys the Object, the entry in the map will be equiv to :
map.put("Bob", null );
So unless there is a cleanup routine that flushes all keys with null values, my map will continue to grow.
So then the question becomes how to fix this? Is there a map construct that I can use which will automatically flush my entry if the value is destroyed?
I contemplated doing something like:
Object user = getUser("Bob");
Map<String, WeakReference<Object>> map = new WeakHashMap<>();
map.put(user.getUsername(), new WeakReference( user ) );
But that seems like a very limited use case where my key has to be an object retrieved from my value. With the WeakHashMap my key cannot be a String constant (ie: "Bob") or there won't be any other references to it, and the GC will clear the object from my map.
Is there some other cache construct that provides all this functionality instead?
You are right in that the collection of the referent does not remove the mapping, however, the result is not equivalent to
map.put("Bob", null );
it will be equivalent to
map.put("Bob", new WeakReference<>(null) );
so you’re not only having a dangling entry instance, but also a dangling cleared WeakReference instance.
When you use
Map<String, WeakReference<User>> map = new WeakHashMap<>();
User user = getUser("Bob");
map.put(user.getUsername(), new WeakReference( user ) );
you get the desired semantics, assuming that user.getUsername() returns a reference to the string instance stored in the User object, to ensure that it stays strongly reachable as long as the User is strongly reachable.
I don’t see any limitation here. Since the string instance stored within the User does exist, there is no overhead in referencing exactly the same string instance as map key as long as the User instance exist. You can still use string constants as lookup key ala User u = map.get("Bob");, as their equality still is determined in terms of String.hashCode() and String.equals(). If you put the mapping using the string constant "Bob" the mapping will usually persist at least as long as the code containing the constant is alive (and each other code that used the same literal during this lifetime), likely the entire application. But where’s the sense in using a different string instance as key than stored in the referent?
Note that WeakHashMap has to deal with the same issue, entries are not removed automatically. It has to use a ReferenceQueue to discover when a referent has been collected to remove its associated entry from the table. This cleanup happens whenever you invoke a method on it, so when you don’t invoke methods on it, the table won’t get cleanup, but since this cleanup happens for every insertion, you are protected against the ever-growing scenario.
if the key isn't a weak reference as well, then once the Object is GC'ed, there will still be an entry in the hash map with the key pointing to null.
With the key pointing to a non-null WeakReference which points to a null referent.
Hence the map will still contain the same number of rows, just all pointing to null values.
No, see above.
The solution is to use a WeakHashMap<String, WeakReference<Object>>, which has a background activity that spots the weak keys being collected, via a ReferenceQueue, and removes the corresponding mappings. Better still, a WeakHashMap<String, WeakReference<User>>. However:
If the keys are string literals they aren't collectable anyway.
It's a bit rich to describe this as a memory leak. The Map itself is a memory leak. If you don't want references to your objects, don't store them in a Map.
Your reasoning is correct.
Map<String,WeakReference<Object>> is used in situations when "lingering" keys do not present a problem, because they are a lot smaller than the objects stored in the map. In situations like that having lots of keys mapped to emptied weak references does not strain the memory resources of the system enough for you to notice.
You can use WeakHashMap<K,V> for situations when you would rather have keys garbage collected, but you are absolutely right about using String constants with it: this would indeed defeat the purpose. Typically, you use a custom key type that overrides equals with reference equality.
For my usecase, I have to pass quite a few context information from different layers/components of the application. Since few of the components are discrete, I am thinking to use ThreadLocal to store such context information. I have an interceptor/filter in place to clean it before the the response is written back to the user. Now, my question is, is it a good idea to use WeakHashMap inside ThreadLocal (see the code snippet below)?
private static final ThreadLocal<Map<String, Object>> context = new ThreadLocal<WeakHashMap<String, Object>>();
The doubt in my mind (with my limited knowledge of Weak references in Java) is, the weak references can return NULL (because GC collects them as per its own will).
Please help me in understanding this. Should I use a strong reference like HashMap or ConcurrentHashMap or my implementation is good to go?
The Javadoc for WeakHashMap states:
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.
So if you can't tolerate entries randomly disappearing, then you really shouldn't be using WeakHashMap with String keys.
I am using a hashmap to store objects with a key that evolves over time.
HashMap<String,Stuff> hm = new HashMap<String,Stuff>()
Stuff stuff = new Stuff();
hm.put( "OrignalKey", stuff);
I didn't find anything better than removing "OrignalKey" and put() a new entry with the same object.
hm.remove("OriginalKey");
hm.put("NewKey", stuff);
remove() seems to be taking a significant cpu toll hence my questions:
What is the actual the memory cost to leave duplicate entries (there is no overlapping risk)?
Am I just missing some neat swapKey() method?
What is the actual the memory cost to leave duplicate entries (there is no overlapping risk)?
Well, you've got an extra entry, and the key itself can't be garbage collected. If the key is "large", that could be a problem. It also means that you'll never be able to get an accurate count, you'll never be able to sensibly iterate over all the values, etc. It seems like a bad idea to me.
Am I just missing some neat swapKey() method?
There's no such thing - and it feels like a fairly rare requirement to me. Any such method would pretty much have to do what you're doing anyway - it has to find the old key, remove it from the data structure, and insert an entry for the new key. I can't easily imagine any optimizations possible just by knowing about both operations at once.
swapping of the key is not easily possible, since the key is used for hashing.
changing the key means that the hashvalue is most probably different, too. in this case, changing the key conforms to deletion and following reinsertion
When a entry in a map has weak key reference, the entry will be removed at the next garbage collection, right?
I can understand that the MapMaker class provides the weakKeys method. But I am confused with the weakValue(). when should I use weakValue or softValue in MapMaker?
You'd use weakValues() when you want entries whose values are weakly reachable to be garbage collected. For an example of when this might be useful... say you have a class that allows users to add objects to it and stores them as values in a Map for whatever reason. This class is typically used as a singleton, so it'll stick around the whole time your application is running. However, the objects the user adds to it aren't necessarily so long-lived. The application will be done with them long before it finishes. You don't want the user to have to manually remove these objects from your class when it is finished with them, but you don't want a memory leak by keeping references to them in your class forever (in other words garbage collection should just work like normal, ignoring your class). The solution is to give the map weakValues() and everything will work as you want.
softValues() is good for caching... if you have a Map<Integer, Foo> and you want entries to to be removable in response to memory demand, you'd want to use it. You wouldn't want to use weakKeys() or softKeys() because they both use == identity, which would cause problems for you (wouldn't be able to get a value with key 300 out because the key you pass in probably wouldn't == the key in the map).
I am working on querying the address book via J2ME and returning a custom
Hashtable which I will call pimList. The keys in pimList {firstname, lastname} maps to an object (we'll call this object ContactInfo) holding (key, value) pairs e.g. work1 -> 44232454545, home1 -> 44876887787
Next I take firstName and add it into a tree.
The nodes of the tree contains the characters from the firstName.
e.g. "Tom" would create a tree with nodes:
"T"->"o"->"m"-> ContactInfo{ "work1" -> "44232454545", "home1" -> "44876887787" }
So the child of the last character m points to the same object instance in pimList.
As I understand it, the purpose of WeakReferences is to that its pointer is weak and the object it points to can be easily GC'ed. In a memory constraint device like mobile phones, I would like to ensure I don't leak or waste memory. Thus, is it appropriate for me to make:
pimList's values to be a WeakReference
The child of node "m" to point to WeakReference
?
It should work. You will need to handle the case where you are using the returned Hashtable and the items are collected however... which might mean you want to rethink the whole thing.
If the Hashtable is short lived then there likely isn't a need for the weak references.
You can remove the items out of the Hashtable when you are done with them if you want them to be possibly cleaned up while the rest of the Hashtable is stll being used.
Not sure I exactly understood what you try to do but an objects reachability is determined by the strongest reference to it (hard reference is stronger than soft reference which is stronger than weak reference which is stronger than phantom reference).
Hard referenced objects won't be garbage collected. Soft referenced objects will be garbage collected only if JVM runs out of memory, weak referenced objects will be garbage collected as soon as possible (this is theory it depends on the JVM and GC implementation).
So usually you use softreference to build a cache (you want to reference information as long as possible). You use weakreference to associate information to an object that is hard referenced somewhere, so if the hardreferenced object is no longer referenced the associated information can be garbage collected - use weakhashmap for that.
hope this helps...
I am not sure if the WeakMap is the right thing here. If you do not hold strong references anywhere in your application, the data in the map will disappear nearly immediately, because nobody is referencing it.
A weak map is a nice thing, if you want to find things again, that are still in use elsewhere and you only want to have one instance of it.
But I might not get your data setup right... to be honest.