muelleth
muelleth

Reputation: 351

Java: efficiently (and safely) remove sequence of elements in a final SortedMap<K,V>?

I'm trying to trace a memory leak in Java. Does this snippet actually prevent the garbage collection.

SortedMap<K,V> cache = ... /* some sorted map */

// and later in a loop, I do

cache = cache.tailMap(someIndex); // does this prevent gc of elements < someIndex

The elements < someIndex are not available anymore in follow-up code. Are they also garbage collected? I'm running into a java.lang.OutOfMemoryError after a while, not sure if this is the reason...

I cannot create a temporary map and reassign like

tmp = new SortedMap<K,V>();
tmp.putAll(cache.tailMap(someIndex));
cache.clear();
cache = tmp; // this breaks the "final"

as I need the original cache object, because it's final. How do I schedule those elements < someIndex for GC, i.e. do I really need to call remove() in a loop? Is there a better way than this?

// "TreeSet" to make sure the keys are ordered
Iterator<K> it = new TreeSet<K>(cache.keySet()).iterator();
while (it.hasNext())
{
    if (it.next() < someIndex) it.remove();
    else break;
}

Edit: SOLVED

Actually the above was not working as expected. Of couse, it was necessary to remove the elements in the map, not within the keyset!

// "TreeSet" to make sure the keys are ordered
Iterator<K> it = new TreeSet<K>(cache.keySet()).iterator();
while (it.hasNext())
{
   K key = it.next();
    if (key < someIndex) cache.remove(key); // not it.remove();
    else break;
}

Edit 2

Most elegant, as suggested by Louis Wassermann below, this is what I was originally after:

cache.headMap(someIndex).clear();

No iterator, no key set, no need to loop - beautiful! Thank you!

Upvotes: 0

Views: 256

Answers (3)

erickson
erickson

Reputation: 269707

Yes, tailMap() creates a view that retains a reference to the parent object. You need to clean up that parent object.

SortedMap<K,V> cache = ... /* some sorted map */
// and later in a loop, DO THIS INSTEAD: 
cache.headMap(someIndex, false).clear(); 

This change through the headMap() view removes unnecessary entries from the underlying cache map.

Upvotes: 0

Alexandre Cartapanis
Alexandre Cartapanis

Reputation: 1523

Javadoc (https://docs.oracle.com/javase/7/docs/api/java/util/SortedMap.html#tailMap(K)) is quite clear : the tailMap() method return a view of the map, backed by the original map. Elements are not removed from the original map, and the original map is still reachable as the new map has a reference on it.

In your case, you must call remove to effectively make objets garbageable.

Upvotes: 1

Sleiman Jneidi
Sleiman Jneidi

Reputation: 23329

If cache is reachable from a GC root, then the map and its keys will not be eligible for garbage collection. What you looking for is a key eviction policy or data-structure.

Guava Cache might be a good option.

Upvotes: 1

Related Questions