Reputation: 3618
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
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
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
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:
Integer
out of the IntegerCache
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
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