How to modify IntegerCache in Java 9

  • 2020-12-05 17:13:43
  • OfStack

Before we begin the body of this article, let's take a look at the following code:

The role of IntegerCache for the Integer class in Java

Package name: java lang

File name: ES13en.java

Method name: IntegerCache

The code for the method is as follows:


private static class IntegerCache { 
static final int high; 
static final Integer cache[]; 

static { 
final int low = -128; 

// high value may be configured by property 
int h = 127; 
if (integerCacheHighPropValue != null) { 
// Use Long.decode here to avoid invoking methods that 
// require Integer's autoboxing cache to be initialized 
int i = Long.decode(integerCacheHighPropValue).intValue(); 
i = Math.max(i, 127); 
// Maximum array size is Integer.MAX_VALUE 
h = Math.min(i, Integer.MAX_VALUE - -low); 
} 
high = h; 

cache = new Integer[(high - low) + 1]; 
int j = low; 
for(int k = 0; k < cache.length; k++) 
cache[k] = new Integer(j++); 
} 

private IntegerCache() {} 
} 

We see in the code that low is -128 and high is 127, so in Java programming, if you want to use an object in the -128 -- 127 range, you use the object in Cache.

The above is a brief introduction to help you understand IntegerCache, and the following is the main text of this article:

The introduction

Five years ago, I published an article on Hungarian on how IntegerCache has changed. This approach is really deep into the Java runtime in scenarios that are not actually used. As you develop this research code, you will have a better understanding of how reflection works and how the Integer classes are implemented.

The Integer class has a private nest called IntegerCache and contains Integer objects with values ranging from -127 to 128.

The Java runtime uses this cache instead of creating a new Integer object when the code needs to be sealed from the int type into an Integer object and the value is within this range. This is primarily a performance optimization concern, and it is important to keep in mind that many int values are in this range (e.g., the subscript index of an array) many times in the program.

The side effect of this is that many times when you use the equal operator to compare two Integer objects, the values are valid as long as they are within the range. This is typical in unit tests. In run mode, code execution fails when the value is greater than 128.

Using reflection to access the IntegerCache class results in 1 odd side effects, note that this affects the entire JVM. If one Servlet redefines the small Integer cache value, then all other Servlet running under the same Tomcat suffer the same problem.

There are 1 other articles on Lukas Eder and Sitepoint that describe this problem.

Now that I've been toying with the early release of Java 9, what I've always had in mind is experimenting with the new VERSION of Java. Before we begin, let's look at what we did in Java 8.

In the Lukas article, I posted his sample code here:


import java.lang.reflect.Field;
import java.util.Random;

public class Entropy {
 public static void main(String[] args)
 throws Exception {

 // Extract the IntegerCache through reflection
 Class << ? > clazz = Class.forName(
  "java.lang.Integer$IntegerCache");
 Field field = clazz.getDeclaredField("cache");
 field.setAccessible(true);
 Integer[] cache = (Integer[]) field.get(clazz);

 // Rewrite the Integer cache
 for (int i = 0; i < cache.length; i++) {
  cache[i] = new Integer(
  new Random().nextInt(cache.length));
 }

 // Prove randomness
 for (int i = 0; i < 10; i++) {
  System.out.println((Integer) i);
 }
 }
}

This code accesses IntegerCache by reflection, then populates the cache with random values (naughty!). .

Let's try to execute the same code in Java 9, and don't expect much fun. Java 9 is more restrictive when someone tries to violate it.


Exception in thread "main" java.lang.reflect.InaccessibleObjectException:
 Unable to make field static final java.lang.Integer[]
 java.lang.Integer$IntegerCache.cache
 accessible: module java.base does not "opens java.lang" to unnamed module @1bc6a36e

The program threw an exception, which would not have happened in Java 8. This is equivalent to saying that the object is not allowed because of the ES91en.base module, which is part of JDK and is automatically imported when each Java program is started, and unnamed modules are not allowed to be opened. This exception is thrown when we try to set the field accessibility property.

Objects that we could easily access in Java 8 are now inaccessible in Java 9 because they are protected by the new module system. Code can only access fields, methods, and other information that can be accessed by reflection if the class is in the same module, or if the module has opened a package for reflection access. This can be achieved through the ES100en-ES101en. java module definition file:


module myModule {
 exports com.javax0.module.demo;
 opens com.javax0.module.demo;
}

This module ES106en.ES107en does not need to be opened for reflection access, especially for unnamed modules. If we create a module and name it, the error message will contain the name of the module.

Can we open the module in the program? java.lang.reflect.Module Modules have an addOpens method to do this.

Feasible?

The bad news for developers: no. It can only open packages in one module in another module, and packages have already been opened in that module by calling this method. This method only allows modules to pass rights to other modules, provided that the other modules have already opened the same package in some way, and not to open packages that have not yet been opened. .

But at the same time, the good news is that Java 9 isn't as easy to crack as Java 8. At least this bug has been closed. It looks like Java is starting to become more of a professional, not just a toy. . In the near future you will be able to seriously move RPG and COBOL projects to Java. (I'm sorry, I'm kidding)

conclusion

The article translation: https: / / dzone com/articles/hacking - the - integercache - in - java - 9


Related articles: