Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 875
Sharp Avatar asked Dec 26 '22 08:12

Sharp


2 Answers

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.

like image 191
Timmos Avatar answered Dec 28 '22 08:12

Timmos


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.

like image 22
Sotirios Delimanolis Avatar answered Dec 28 '22 08:12

Sotirios Delimanolis