Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove keys when values are nullified #2

Open
jimka2001 opened this issue Jun 14, 2019 · 11 comments
Open

Remove keys when values are nullified #2

jimka2001 opened this issue Jun 14, 2019 · 11 comments

Comments

@jimka2001
Copy link

I don't really understand the Java implementation of WeakValueHashMap. I'm using this in a Scala project. The behavior in Scala is that when values become otherwise unreferenced, then the hash map value is nullified, i.e., the object is removed from he value position and is replaced by null. For my application it would be much better if the key/value pair were simply removed from the hash map.

Can I achieve this with this class? I'd be happy if it worked this way automatically, or if there were a Boolean flag on the constructor to specify this behavior, or even if I could register a callback which would be called with the hash map and key as argument whenever a value has been (or is about to be) nullified. That would give me a chance to remove the key myself.

Otherwise, it is not really feasible (in terms of runtime complexity) to search the hash table periodically for null values, and remove the corresponding keys.

I'd appreciate either advise, enhancement, or a clue about how to enhance the code. I'm happy to create a PR, but I'm not a java-ist. I've never written a line of java in my life.

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

I wrote this class to use as a very very simple object cache.

The HashMap would keep values in memory as long as said value is hard-referenced anywhere else in the application. Once all references in the application are gone, only a WeakValue<> reference remains in the HashMap and the value is kept in memory as long as the JVM garbage collector keeps it in memory. If I need the same key some time later, I could see if it's still in memory and avoid re-calculating the value.

The JVM garbage collector is designed to keep WeakValue<> values in memory unless low-mem pressure requires it to free memory for other use, which is exactly what I wanted for my lazy memory cache.

Does this help?

@jimka2001
Copy link
Author

No, not really. I think I understand your motivation, but I didn't understand how it addresses my issue. The answer might be, "No, sorry, it is not feasible to enhance WeakValueHashMap to remove keys when values are nullified."

This was a feature of the weak hash table I was using in SBCL Common Lisp. In that system, there is even a debugging callback, which notifies the application before data is garbage collection. This is helpful for making sure the data is really being cleared out.

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

This code is 6yrs old and I have to admit that I can't help you much.

There is an internal processQueue() method that is used as a cleanup task to remove those hash entries that the garbage collector has removed from memory by the time you try to access it.

Back when I used this code in a small Java project, WeakValueHashMap correctly removed unreferenced values from the HashMap and didn't return null values for them, provided that you use WeakValueHashMap's access methods like get/put/size/values etc.

@jimka2001
Copy link
Author

jimka2001 commented Jun 14, 2019

Ahh, interesting. I appreciate that it is difficult to remember work done 6 years ago. I don't even remember what I did yesterday sometimes.

Just to make sure I understand what you are saying. What do you mean by "removed unreferenced values"? Do you mean that the corresponding key was removed from the map, or only the value was removed. If only the value was removed, and not the key, then how did you prevent null from being returned when you access a key whose value was removed?

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

I guess I now understand the issue.

Returning null for a non-existing value is the defined behaviour for get() for any implementation of a HashMap:

public V get(Object key)

get(key): Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
[..]
A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases.

Thus, if the value has been removed from memory by the time you try to access it, it will always be null if you access it via get(). The entry will not appear though if you check with containsKey() or values().

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

Problem is that if you do something akin to if hashmap.containsKey(key) { v = hashmap.get(key) } you're in for a race condition, of course.

My original use for this code was for an application with no null values stored in that hashmap. Again, this was meant as a very quick hack for a really simple in-memory cache. It served me well, but a proper memory cache would use a more sophisticated cache scheme.

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

Just to make sure I understand what you are saying. What do you mean by "removed unreferenced values"? Do you mean that the corresponding key was removed from the map, or only the value was removed. If only the value was removed, and not the key, then how did you prevent null from being returned when you access a key whose value was removed?

WeakValueHashMap keeps all values referenced using a WeakReference(). If there is no normal reference left to a value, such a weakly referred value may be cleaned up by the garbage collector. The garbage collector queue will announce its intent to remove an object from memory on its next run, therefore processQueue() removes those hash entries - both key and the weak reference to the value - from WeakValueHashMap.

@jimka2001
Copy link
Author

jimka2001 commented Jun 14, 2019

Ah, this is interesting. Because from the Scala side, If I access the non-existent value in WeakValueHashMap which was never in the hash map, I get None, but if I access a non-existent value which used to be there but has been removed on the java side, then I get Some(null). I'm not expert enough to know whether I'm finding a bug in your code, or a bug in the Scala interface to Java, or elsewhere. I need to figure out a way to test.

However, if I understand you correctly you are saying that if keys are remaining in the hash, but now associated with null values, then it is a bug in WeakValueHashMap? In which case I could try to submit a PR with a fix?

@jimka2001
Copy link
Author

BTW, one thing I didn't mention. I was very happy to find this class implemented. I find it genial that it exists. And don't interpret my questions as complaint.

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

I don't know enough about Scala and also, this code is 6yrs old and it's possible that things have changed on the Java side by now, too. It's very well possible that my old code is buggy based on today's Java standards.

@hzulla
Copy link
Owner

hzulla commented Jun 14, 2019

(Or that it was buggy from the start. Ahem.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants