ForguesR
ForguesR

Reputation: 3618

Disabling Java Integer Cache

Recently stumbled on a problem related to the Java Integer Cache and I'm looking for a way to disable it.

Right now testing is impossible because we can't say it works unless we test with an integer value outside of the cache.

Our case : We had 8 buggy != comparisons and everything worked fine during 2 months of testing because we never had an underlying database entity with a PK higher than 128.

Upvotes: 4

Views: 583

Answers (4)

user8864873
user8864873

Reputation:

Since Java 6 introduced Escape Analysis I see no benefit in pooling temporary objects such as Integer. Moreover, Integer cache being "unpredictable" sometimes can prevent Escape Analysis from functioning properly.

Java 8 Escape Analysis can't do scalar replacement of Integer objects if Integer cache is enabled. (Java 13 seems to be doing well either way)

Disabling Integer cache is not supported officially, but it can be done with this code snippet:

public static Unsafe getUnsafe() {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe) theUnsafe.get(null);
    } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
        return null;
    }
}

public static void disableIntegerCache() {
    try {
        Class<?> clazz = Class.forName("java.lang.Integer$IntegerCache");
        Field cache = clazz.getDeclaredField("cache");
        Field low = clazz.getDeclaredField("low");
        Field high = clazz.getDeclaredField("high");

        Unsafe unsafe = getUnsafe();

        Object base = unsafe.staticFieldBase(low);
        long lowOffset = unsafe.staticFieldOffset(low);
        long highOffset = unsafe.staticFieldOffset(high);
        long cacheOffset = unsafe.staticFieldOffset(cache);

        unsafe.putObjectVolatile(base, cacheOffset, new Integer[0]);
        unsafe.putIntVolatile(base, lowOffset, Integer.MAX_VALUE);
        unsafe.putIntVolatile(base, highOffset, Integer.MIN_VALUE);
    } catch (ClassNotFoundException | NoSuchFieldException | IllegalArgumentException e) {
        e.printStackTrace();
    }
}

I use MAX_VALUE and MIN_VALUE because of weird behavior related to JVM intrinsics in valueOf method.

Upvotes: 0

Michael
Michael

Reputation: 44150

You don't. The integer cache is an implementation detail of Integer. If you use Integer, you accept the cache. It's a good thing, there to reduce the number of effectively duplicate objects which may be created.

Use FindBugs to locate spurious integer reference comparisons and replace them with Integer.equals -- that is the proper way to fix the problems you're encountering.

If problems which are this fundamental to the language are present in your application then I strongly suggest running a full suite of static analysis tests and working your way through to triage them.

Upvotes: 8

Makoto
Makoto

Reputation: 106430

The simplest thing to do to address and catch this kind of bug is to write a unit test that has a primary key arbitrarily (and randomly) higher than 128.

This gives you:

  • An Integer out of the IntegerCache
  • A way to independently validate this without having to change any runtime parameters (i.e. easily repeatable on developer boxes)
  • Concise documentation in the form of tests that states that you want to be sure that these values are actually being accounted for

You do not need to disable JVM internals to test this. You just need to be aware of their limitations and test around them if their case poses a challenge for your application.

Upvotes: 1

AR1
AR1

Reputation: 5005

You can set the size of the cache using Java Options. The following will let you set the cache as you wish:

-XX:AutoBoxCacheMax=<CACHE_SIZE>

for instance:

-XX:AutoBoxCacheMax=1

Upvotes: -2

Related Questions