It all started with the fact that once again I was picking a memory dump of a Java application in
Eclipse Memory Analyzer and saw such an interesting thing:

I am quite familiar with the HashMap code, but I never saw the FrontCache nested class there. Maybe with the latest JDK update they sent me an updated HashMap? I looked at the source, but the words “front” were not found there. It became interesting where this class comes from and what it does.
Rummaging through the JRE (I have 1.7u10, but in the last 1.6 I also have it), I found a curious jarik: alt-rt.jar, in which HashMap $ FrontCache.class was found, as well as several other classes (LinkedHashMap, TreeMap, BigDecimal, BigInteger, MutableBigInteger and their nested classes). Usually these classes are connected from rt.jar. Why did they start to be loaded from this mysterious jarik?
I remembered that I recently experimented with Java machine options and, in particular, included -XX: + AggressiveOpts. On the website of Orakla about this key
is written sparingly:
Turn in point performance compiler optimizations
On Habré
was an attempt to explain this option in more detail, they say, this is a combination of other keys. Having rummaged in source codes of
OpenJDK of the 7th version I understood that business is not limited to keys. This is what we see in hotspot / src / share / vm / runtime / arguments.cpp:
jint Arguments::parse_vm_init_args(const JavaVMInitArgs* args) { ... if (AggressiveOpts) {
Aha With this option, alt-rt.jar is really added. You can verify this from your own application using
System.getProperty("sun.boot.class.path")
. Thus, for the mentioned classes, when AggressiveOpts is enabled, the implementation changes.
But what is the difference in the implementation of HashMap? I began to look for a modified source, but then I suffered a failure. It turned out that this jar is built using jdk / make / altclasses / Makefile, and the source directory is designated as
ALTCLASSES_SRCDIR = $(CLOSED_SRC)/share/altclasses
This smell is not very good, and the jdk / make / common / Defs.gmk file confirmed my concerns:
Of course, the specified directory is not included. Just in case, I deflated JDK 8, but the situation there was no better. Oracle hides an alternative HashMap.
')
Rummaging through the Internet, I ran into a
project that reassured me, but in vain. It is written on the main page that there are source codes of classes from jre \ lib \ alt-rt.jar, in fact there is a standard implementation of HashMap and other classes. Apparently, the author did not understand that there are two options.
There remains one way - to disassemble the bytecode (javap -c -private) and read it like this. To make it easier, I disassembled the usual and alternative HashMap, with a pair of regexs I threw out non-essential things and compared them to diff. At first, everything looked pretty scary, but then I guessed that the normal and alternative HashMap code evolved independently, so you need to compare the alternative HashMap with a common ancestor, which turned out to be HashMap from the latest updates of the 6th JDK. Here the picture has become much clearer, it did not even require special tools for decompiling into Java code. In HashMap, HashMap.FrontCache frontCache really appeared, which is initialized in the constructor. The FrontCache constructor accepts capacity — the number of elements in the main hash table. A code like this is added to the get (Object key) method:
if(frontCache != null) { V value = frontCache.get(key); if(value != null) return value; }
The following was added to the put method (K key, V value):
if(frontCache != null) { frontCache.put(key, value); }
There are technical changes in other methods, but the essence is here. It is clear that frontCache is some kind of alternative (apparently faster) data storage. When an item is requested, it is first searched in the frontCache, and when a new item is entered, it is entered in the frontCache, and in a regular hash table. What does the FrontCache class accelerate? Here are the most important methods from it:
private class FrontCache { private Object[] cache; private int bitMask; public FrontCache(int capacity) { this.cache = new Object[capacity]; this.bitMask = makeBitMask(capacity); } public int makeBitMask(int capacity) { return -1 << (32 - Integer.numberOfLeadingZeros(capacity - 1)); } public boolean inRange(int value) { return (value & bitMask) == 0; } public V get(Object key) { if(key instanceof Integer) { int intKey = ((Integer)key).intValue(); if(inRange(intKey)) { return (V) cache[intKey]; } } return null; } private void put(K key, V value) { if(key instanceof Integer) { int intKey = ((Integer)key).intValue(); if(inRange(intKey)) { cache[intKey] = value; } } } }
Other methods are used to delete items, change cache size, etc. But the idea from the above code is clear: if the key is Integer and it falls in the range from 0 to capacity-1 (the test is originally optimized), then the value is simply entered into the array by an index with a corresponding sequence number without any transformations and hash functions.
If the keys are not integers, then FrontCache is simply useless. However, the array is still allocated and checks are made for each operation with HashMap.
In fact, even more memory is lost, since HashMap.Entry has a setValue method, which should now update the value in FrontCache if necessary. Therefore, a link to the HashMap itself is added to each Entry, which can add up to 8 extra bytes per record.
The discovery was a bit shocking to me. Lately, we don’t use keys like Integer for the glory of
Trove , so it turned out that optimization only eats time and memory in vain. In general, I decided that AggressiveOpts should be disabled. Of course, I didn’t understand what had changed there in the TreeMap and math classes (in LinkedHashMap, cosmetic changes related to changes in HashMap.Entry to support FrontCache). Be careful and do not use options whose meaning you do not fully understand.