Twitter

Wednesday, March 31, 2010

Java - Cache (frequently used immutable objects) using ConcurrentHashMap.

One of the better way to reduce the impact of garbage collection in any Java application is to reduce the number of newly created objects thereby reducing the amount of garbage produced.

Let us take an example of university admission application. There are two possible titles (Masters and Bachelors) and two possible majors (Computer and Electrical). Let us name the combination of title-and-major as Degree.

Assume that there are thousands of applications submitted every day. Each application will be for the particular combination of the above mentioned Degree. eg. Masters-Computer, Master-Electrical, Bachelor-Computer...

Instead of creating new Degree object for each and every application, we can cache the Degree object. When a particular Degree object is demanded, look for that in the cache. If the cache doesn't contain that, then create it, put it inside the cache and then return it. Since the cache is built using ConcurrentHashMap, it is also thread safe. i.e, Even when there are more than 1 thread running the Degree.valueOf() method for a same set of "title" and "major" strings, ONLY one instance of that particular Degree instance will be constructed and will be used by the threads.

It is clear that we will produce less garbage using caching. But is there is another add-on advantage. It is easier to check whether two Degree objects are equal using reference equality (==) rather that equals() method. ie. we can do Master_Computer==Master_Computer_ANOTHER instead of Master_Computer.equals(Master_Computer_ANOTHER). On my machine == is 9 times faster than equals(). Thereby saving some CPU cycles.

Have a look in the following code on how to build a cache of Objects (Degree) having two String properties. Complete code here.

The main method is the Degree.valueOf() method. Where all the caching is done.

This is just an example. This technique can be used in the telco application servers running with load in terms of 1000s of TPS. Eg. Consider a header/value type of protocol. Instead of creating a particular header 1000s of times per second we can reuse the existing cached header thereby relaxing the CPU and RAM for other useful processing.



......

        Degree MASTER_COMPUTER = Degree.valueOf(MASTER, COMPUTER);
        Degree MASTER_ELECTRICAL = Degree.valueOf(MASTER, ELECTRICAL);
        Degree BACHELOR_COMPUTER = Degree.valueOf(BACHELOR, COMPUTER);
        Degree BACHELOR_ELECTRICAL = Degree.valueOf(BACHELOR, ELECTRICAL);
......

        // we ask the cache for all the possible present values that were created by the above lines.
        // Therefore it returns the existing values; Nothing is created anew.
        System.out.println("\nNo more new constructions for existing entries...");
        Degree MASTER_COMPUTER_1 = Degree.valueOf(MASTER, COMPUTER);

......
        // now ask for something that is not there; cache will create it anew and caches them
        System.out.println("\nNew constructions for non-existing entries...");
        Degree.valueOf("Phd", "Computer");

......    
        // one more advantage of caching : we can compare the reference of two objects instead of checking their equal()ity
        // this is because all requests to Degree.valueOf(MASTER, COMPUTER) always return the very same object
        System.out.println("\nFaster equality check...");

        

Output
New Degree object: Master_Computer
New Degree object: Master_Electrical
New Degree object: Bachelor_Computer
New Degree object: Bachelor_Electrical

No more new constructions for existing entries...
Returning existing instance of Master_Computer

New constructions for non-existing entries...
New Degree object: Phd_Computer

Faster equality check...
Checked with equals().
Checked with reference equality.

No comments:

Post a Comment