26

I was digging into how the Integer class actually uses cached objects, and I found the below code in the Integer.valueOf method:

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

My question is:

psmears
  • 26,070
  • 4
  • 40
  • 48
Vishrant
  • 15,456
  • 11
  • 71
  • 120
  • Not exact duplicate, but [relevant info about how this implementation evolved](http://stackoverflow.com/questions/15052216/how-large-is-the-integer-cache). – biziclop Nov 24 '15 at 10:37
  • 1
    Note the comment in the source code (1.8.0_65): `//range [-128, 127] must be interned (JLS7 5.1.7)` followed by `assert IntegerCache.high >= 127;` – Alexis C. Nov 24 '15 at 10:39
  • “assert provides an effective way to detect and correct programming errors”—right. “But this is runtime code”—where’s the contradiction? Why should runtime code not use effective ways to detect and correct programming errors? – Holger Nov 24 '15 at 11:50
  • @Holger according to the code, it is not possible that `AssertionException` will be raised as value will be 127. I did not find any reason why `assert` was used, that is why I asked this question. – Vishrant Nov 24 '15 at 11:53
  • 2
    It’s used for the same reason it should be used: to document what should always be true. Of course, it should not be possible that an `AssertionError` will be raised, that’s the whole point. Why do you think anyone would ever *want* an `AssertionError` to be thrown? – Holger Nov 24 '15 at 11:56
  • 1
    @Holger, it always bugs me that code coverage tools like JaCoCo mark `throw new AssertionError()` line as non-covered. It's intended to be non-covered! – Tagir Valeev Nov 26 '15 at 11:15

4 Answers4

17

The purpose of asserts is to establish invariants and to document the implementation. Here, this assert documents the fact that when the valueOf method is entered IntegerCache.high value is guaranteed to be at least 127. It's better to write an assert instead of the comment, because the assert will also be checked by the JVM when the corresponding command line option (-esa) is active.

Normally this assert will never throw, because the IntegerCache.high is initialized in this way:

int h = 127;
String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
    try {
        int i = parseInt(integerCacheHighPropValue);
        i = Math.max(i, 127);
        // Maximum array size is Integer.MAX_VALUE
        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    } catch( NumberFormatException nfe) {
        // If the property cannot be parsed into an int, ignore it.
    }
}
high = h;

This code guarantees that the value will be at least 127. So the assert can throw if you modify IntegerCache.high (using reflection and setAccessible(true)) or if the JDK will be modified in the future and bug will be introduced in IntegerCache.high initialization code. That's why asserts exist: to catch bugs.

LThode
  • 1,843
  • 1
  • 17
  • 28
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • 2
    The assert can also throw if it is invoked during VM initialization when IntegerCache.high is equal to 0. – emory Nov 24 '15 at 14:30
9

The JLS mandates that the integer cache must be in place for integers between -128 and 127.

At present, the Oracle implementation enforces this but no more, so the cache ceiling could well be extended at some point in the future (unlikely in practice, but it'd be completely in spec.)

However, it can never be less than 127, otherwise the implementation would no longer conform to the JLS - and that would be a pretty big deal, hence (I believe) why the assert statement is there!

As biziclop pointed out in the comments, it can also be changed by the user by passing a VM argument - another (and perhaps more compelling) reason the assertion is in place.

Also note that the assert statement will be skipped unless running with -ea, so in normal use there is absolutely no runtime overhead for this check.

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
6

The cache, normally for -128 ... 127, can be enlarged, which might be the reason: it should not become less. And as the code would function even without cache, an assert might be a soft way: telling about a performance defect when developing. As asserts do no impact on production code.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 3
    The assert ensures that the code conforms to the JLS, which explicitly mandates that the cache be at least that size. – biziclop Nov 24 '15 at 10:35
  • I think you can use reflection to make IntegerCache.high anything you want. I am not sure how that would affect this code. – emory Nov 24 '15 at 10:35
  • @biziclop yes, a kind of implementation compatibility IMHO. Worth to be mentioned w.r.t. language specification: `Integer.valueOf(127) == Integer.valueOf(127)` so to say. – Joop Eggen Nov 24 '15 at 10:41
  • 1
    @emory The low border always is -128, but the high can be configured. As already generics with primitive types are in the pipeline of some future java, in a couple of years this may become an obsolete technique, and we'll use only `int` . – Joop Eggen Nov 24 '15 at 10:46
2

Looking at

http://www.docjar.com/html/api/java/lang/Integer.java.html

I see this comment

       /*
        * WARNING: This method may be invoked early during VM initialization
        * before IntegerCache is initialized. Care must be taken to not use
       * the valueOf method.
       */

If they had used valueOf in parseInt then when parseInt is called early in VM initialization, the high value would actually be zero.

I am not sure what problems that would cause, but evidently someone thought it was worth guarding against.

  1. what is the use of assert IntegerCache.high >= 127; I read that assert provides an effective way to detect and correct programming errors. But this is runtime code why someone will use assert?

To guard against code invoked during VM initialization calling the valueOf method.

  1. And when it will throw AssertionError in this scenario?

If you run java with the -ea option (enabling assertions) and there is some initialization code that calls valueOf the JVM will almost immediately crash because of AssertionError. Hopefully the Java team tests for this stuff and will fix the offending code before releasing that to the wild.

Contrary to other answer's assertions that high will always be greater than or equal to 128, it appears that there are times it is zero (not counting reflection).

emory
  • 10,725
  • 2
  • 30
  • 58