Sharp
Sharp

Reputation: 53

Behaviour of weakhashmap with String literal and String object

I am understanding the concept of WeakhashMap. String literal and String object made it difficult to understand.

Following is the code:

package com.lnt.StringBuf;

import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

public class Test1 {
    public static void main(String[] args) {

        Map w = new WeakHashMap();
        Map h = new HashMap<>();

        String hkey = new String("hashkey");
        String wkey = new String("weakkey");
    /*  String hkey = "hashkey";
        String wkey = "weakkey";*/

        h.put(hkey, 1);
        w.put(wkey, 1);

        System.gc();

        System.out.println("Before");
        System.out.println("hashmap size: " + h.size());
        System.out.println("weakmap size: " + w.size());
        System.out.println("Hashmap value: " + h.get("hashkey") + "\t"
                + "weakmap value: " + w.get("weakkey"));

        hkey = null;
        wkey = null;

        System.gc();
        System.out.println(hkey+" "+wkey);

        System.out.println("After");
        System.out.println("hashmap size: " + h.size());
        System.out.println("weakmap size: " + w.size());
        System.out.println("Hashmap value: " + h.get("hashkey") + "\t"
                + "weakmap value: " + w.get("weakkey"));

        System.out.println(h.entrySet());
        System.out.println(w.entrySet());

    }

}

Output is:

Before
hashmap size: 1
weakmap size: 1
Hashmap value: 1    weakmap value: 1
null null
After
hashmap size: 1
weakmap size: 0
Hashmap value: 1    weakmap value: null
[hashkey=1]
[]

But when String hkey = new String("hashkey"); String wkey = new String("weakkey");

is replaced with following code, output changes.

String hkey = "hashkey";
String wkey = "weakkey";

Output is:

Before
hashmap size: 1
weakmap size: 1
Hashmap value: 1    weakmap value: 1
null null
After
hashmap size: 1
weakmap size: 1
Hashmap value: 1    weakmap value: 1
[hashkey=1]
[weakkey=1]

Question: Making String literal and String object 'null' impacts in different way in WeakHashMap. What is the reason?

Upvotes: 4

Views: 1527

Answers (3)

Timmos
Timmos

Reputation: 3324

Strings literals are interned, which basically means that there is a cache, mostly referred to as the String pool. So String literals are always strongly referenced - making them not suitable to use as keys in a weak structure e.g. WeakReference and WeakHashMap.

The same goes for autoboxed ints: also Integer keeps a cache of Integer objects for int values in the range [-128, 127]. So you should also not be using int for keys in a weak structures.

However, you can work around these problems by creating a new object when inserting an entry, e.g. in the following example, the "a" entry will eventually be removed from the map, but the "b" entry will stay in there forever which is infact a memory leak:

WeakHashMap<String, Object> map = new WeakHashMap<>();
map.add(new String("a"), new Object());
map.add("b", new Object());

The same example is valid for integers:

WeakHashMap<Integer, Object> map = new WeakHashMap<>();
map.add(new Integer(56), new Object());
map.add(57, new Object());

Here the entry for 57 will stay in there forever, because of Integer's caching, but the 56 entry can be removed by the garbage collector.

Also Boolean has caching, but only for 2 values of course. Where Integer has 256 potentially dangerous values, String practically has unlimited occurences that can be dangerous - you only have to use literals (or use String.intern()) to create them. It might be that there exist other dangerous classes, too.

Upvotes: 8

Deepa Bhatia
Deepa Bhatia

Reputation: 21

String is not suitable for WeakHashMap if used as a key. This is because, when a String object is created JVM creates the object in the String pool also. They may remain strongly referenced in the string pool even after you have nullified the reference.

Map<String, String> stringWeakHashMap = new WeakHashMap<String, String>();
String str1 = "Key 1";
String str2 = "Key 2";

stringWeakHashMap.put(str1, "Value 1");
stringWeakHashMap.put(str2, "Value 2");
str1 = null;

System.gc();
System.out.println("Weak Hash Map :" + stringWeakHashMap.toString());

After running the above code the output generated is

Weak Hash Map :{Key 2=Value 2, Key 1=Value 1}

Upvotes: 0

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279990

Making String literal and String object 'null' impacts in different way in WeakHashMap. What is the reason?

First, you can't make an object null. You can make a variable reference null or reference an object, but making an object null is not a concept that exists.

The javadoc of WeakHashMap states

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.

At runtime, the JVM creates an String object for every String literal it sees while loading classes. These objects cannot be GC'ed until the ClassLoader that loaded them is GC'ed, regardless of them being referenced in a WeakHashMap.

This would be similar to doing

String wkey =  new String("weak");
String other = wkey;

Since you have a reachable reference to the object somewhere else, it cannot be GC'ed, even if used in a weak collection.

Also note that System.gc() does not guarantee that Garbage Collection will run. Be mindful of that when using it as to not misinterpret results.

Upvotes: 2

Related Questions