0

I have a requirement where I have to use a hashMap where values can change dynamically and keys can get only inserted not deleted or updated. However Keys will be read many times and values may be updated many times.

For latency issues, I haven't made entire HashMap as ConcurrentHashMap in which case while writing keys I will have issues. I am fine with compromising slight data accuracy with keys. So I decided to keep my own dirty copy of keys. A code snippet like below.

package com.test.collections;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

public class CheckKeySet {

    private static Map<String, ConcurrentHashMap<String, String>> testMap = new HashMap<String, ConcurrentHashMap<String, String>>();

    public static void main(String[] args) {
        CheckKeySet trial = new CheckKeySet();
        try {
            trial.doIt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void doIt() throws InterruptedException {
        Thread accessThread1 = new AccessKeySet("Access1");
        Thread writeThread = new WriteValueSet();
        Thread accessThread2 = new AccessKeySet("Access2");
        Thread accessThread3 = new AccessKeySet("Access3");

        writeThread.start();
        Thread.sleep(1000);
        accessThread1.start();
        Thread.sleep(2000);
        accessThread2.start();
        Thread.sleep(4000);
        accessThread3.start();
    }

    private Set<String> getKeySet() {
        return new TreeSet<String>(testMap.keySet());
    }

    class AccessKeySet extends Thread {

        public AccessKeySet(String string) {
            super(string);
        }

        @Override
        public void run() {
            Set<String> keySet = getKeySet();
            System.out.println("###############Trying to print########## " + getName() + " keySet size " + keySet.size());
            for (String s : keySet) {
                System.out.println(s);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class WriteValueSet extends Thread {

        @Override
        public void run() {
            int i = 1;
            for (i = 1; i < 10000; i++) {
                testMap.put("Check-" + i, new ConcurrentHashMap<String, String>());
                System.out.println("Inserted " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

My question, I am creating new Set Object in above implementation with the HashMap.keySet() as base value. HashMap.keySet() will live forever in my code. Will this impact the GC of dirty set objects that I am creating in getKeySet() ? I want this new set object to be cced whenever its holding object becomes eligible for gc. It should not be stopped from getting gced because hashMap.keySet is alive

Holger
  • 285,553
  • 42
  • 434
  • 765
Rengasami Ramanujam
  • 1,858
  • 4
  • 19
  • 29
  • 4
    *"For latency issues, I haven't made entire HashMap as ConcurrentHashMap"* What latency issues does `ConcurrentHashMap` have over `HashMap`? Please show measured performance numbers that back wasting so much time coding something that you can get for free using `ConcurrentHashMap`. See http://stackoverflow.com/a/32141829/5221149 – Andreas Apr 28 '17 at 04:20
  • Point is I am using a mono-core system. So, I dont have the luxury of using multiple threads play around. I think that answered your question regarding latency – Rengasami Ramanujam Apr 28 '17 at 08:21
  • You phrase “compromising slight data accuracy with keys” is a huge understatement. Your code is fundamentally broken and having a single core or `sleep` calls doesn’t fix that. It is moot to discuss any other aspect of such fundamentally broken code. Also, it doesn’t make any sense to create multiple threads at all, if the sequential execution is the only one that wouldn’t break (but may still be subject to visibility issues). But besides that, it’s not clear what side effect of calling `keySet()` on a `HashMap` you expect. It’s the last thing you should worry about in your code. – Holger Apr 28 '17 at 11:19

1 Answers1

0

Correct me if I'm wrong, but it sounds like you don't want keys or values to get garbage collected from testMap, unless you manually remove them. Rather, you only want keys in the key set returned by your getKeySet() method to be garbage collected, if at all possible, even you might not remove them explicitly from the set.

Assuming that is the case, you should consider using weak references. More precisely to your implementation, you could simulate a WeakHashSet (as per this answer) and change your getKeySet() method implementation to:

private Set<String> getKeySet() {
  Set<String> cache = Collections.newSetFromMap(
    new WeakHashMap<String, Boolean>());
  cache.addAll(this.testMap.keySet());
  return cache;
}

This new code copies all of the keys of the strongly referenced testMap into the weakly referenced cache. Thus, whenever the garbage collector runs after you remove an element from testMap, the garbage collector will remove that same element automatically from cache.

Community
  • 1
  • 1
entpnerd
  • 10,049
  • 8
  • 47
  • 68