program story

자바의 이상한 정수 복싱

inputbox 2020. 8. 12. 08:16
반응형

자바의 이상한 정수 복싱


다음과 유사한 코드를 보았습니다.

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a == b);

        Integer c = 100, d = 100;
        System.out.println(c == d);
    }
}

실행되면 다음 코드 블록이 출력됩니다.

false
true

첫 번째 이유를 이해 false합니다. 두 개체가 별도의 개체이기 때문에 ==참조를 비교합니다. 그러나 두 번째 진술이 왜 반환되는지 알 수 없습니다 true. Integer의 값이 특정 범위에있을 때 시작되는 이상한 오토 박싱 규칙이 있습니까? 여기서 무슨 일이 일어나고 있습니까?


true라인은 실제로 언어 사양에 의해 보장된다. 에서 섹션 5.1.7 :

boxing되는 값 p가 true, false, a byte, \ u0000 ~ \ u007f 범위의 char 또는 -128에서 127 사이의 int 또는 짧은 숫자이면 r1과 r2를 두 개의 boxing 변환의 결과로 둡니다. 의 p. 항상 r1 == r2 인 경우입니다.

두 번째 출력 라인은 보장되지만 첫 번째 라인은 보장되지 않는다는 것을 제안합니다 (아래 인용 된 마지막 단락 참조).

이상적으로 주어진 기본 값 p를 박싱하면 항상 동일한 참조가 생성됩니다. 실제로 이것은 기존 구현 기술을 사용하여 실현 가능하지 않을 수 있습니다. 위의 규칙은 실용적인 타협입니다. 위의 마지막 절에서는 특정 공통 값을 항상 구별 할 수없는 개체로 묶어야합니다. 구현은 이들을 느리게 또는 열심히 캐시 할 수 있습니다.

다른 값의 경우,이 공식은 프로그래머 측에서 박스형 값의 식별에 대한 어떠한 가정도 허용하지 않습니다. 이렇게하면 이러한 참조의 일부 또는 전체를 공유 할 수 있습니다 (필수는 아님).

이렇게하면 특히 작은 장치에서 과도한 성능 저하없이 대부분의 일반적인 경우 동작이 원하는 동작이됩니다. 예를 들어 메모리 제한이 적은 구현은 -32K-+ 32K 범위의 정수 및 long뿐만 아니라 모든 문자와 단락을 캐시 할 수 있습니다.


public class Scratch
{
   public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;  //1
        System.out.println(a == b);

        Integer c = 100, d = 100;  //2
        System.out.println(c == d);
   }
}

산출:

false
true

네 첫 번째 출력은 참조를 비교하기 위해 생성됩니다. 'a'와 'b'-이들은 서로 다른 두 참조입니다. 포인트 1에서는 실제로 다음과 유사한 두 개의 참조가 생성됩니다.

Integer a = new Integer(1000);
Integer b = new Integer(1000);

The second output is produced because the JVM tries to save memory, when the Integer falls in a range (from -128 to 127). At point 2 no new reference of type Integer is created for 'd'. Instead of creating a new object for the Integer type reference variable 'd', it only assigned with previously created object referenced by 'c'. All of these are done by JVM.

These memory saving rules are not only for Integer. for memory saving purpose, two instances of the following wrapper objects (while created through boxing), will always be == where their primitive values are the same -

  • Boolean
  • Byte
  • Character from \u0000 to \u007f (7f is 127 in decimal)
  • Short and Integer from -128 to 127

Integer objects in some range (I think maybe -128 through 127) get cached and re-used. Integers outside that range get a new object each time.


Yes, there is a strange autoboxing rule that kicks in when the values are in a certain range. When you assign a constant to an Object variable, nothing in the language definition says a new object must be created. It may reuse an existing object from cache.

In fact, the JVM will usually store a cache of small Integers for this purpose, as well as values such as Boolean.TRUE and Boolean.FALSE.


That is an interesting point. In the book Effective Java suggests always to override equals for your own classes. Also that, to check equality for two object instances of a java class always use the equals method.

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a.equals(b));

        Integer c = 100, d = 100;
        System.out.println(c.equals(d));
    }
}

returns:

true
true

My guess is that Java keeps a cache of small integers that are already 'boxed' because they are so very common and it saves a heck of a lot of time to re-use an existing object than to create a new one.


In Java the boxing works in the range between -128 and 127 for an Integer. When you are using numbers in this range you can compare it with the == operator. For Integer objects outside the range you have to use equals.


Direct assignment of an int literal to an Integer reference is an example of auto-boxing, where the literal value to object conversion code is handled by the compiler.

So during compilation phase compiler converts Integer a = 1000, b = 1000; to Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);.

So it is Integer.valueOf() method which actually gives us the integer objects, and if we look at the source code of Integer.valueOf() method we can clearly see the method caches integer objects in the range -128 to 127 (inclusive).

/**
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
 public static Integer valueOf(int i) {
     if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }

So instead of creating and returning new integer objects, Integer.valueOf() the method returns Integer objects from the internal IntegerCache if the passed int literal is greater than -128 and less than 127.

Java caches these integer objects because this range of integers gets used a lot in day to day programming which indirectly saves some memory.

The cache is initialized on the first usage when the class gets loaded into memory because of the static block. The max range of the cache can be controlled by the -XX:AutoBoxCacheMax JVM option.

This caching behaviour is not applicable for Integer objects only, similar to Integer.IntegerCache we also have ByteCache, ShortCache, LongCache, CharacterCache for Byte, Short, Long, Character respectively.

You can read more on my article Java Integer Cache - Why Integer.valueOf(127) == Integer.valueOf(127) Is True.


In Java 5, a new feature was introduced to save the memory and improve performance for Integer type objects handlings. Integer objects are cached internally and reused via the same referenced objects.

  1. This is applicable for Integer values in range between –127 to +127 (Max Integer value).

  2. This Integer caching works only on autoboxing. Integer objects will not be cached when they are built using the constructor.

For more detail pls go through below Link:

Integer Cache in Detail


If we check the source code of Integer obeject, we will find the source of valueOf method just like this:

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

which can explain why Integer objects, which in the range from -128 (Integer.low) to 127 (Integer.high), are the same referenced objects during the autoboxing. And we can see there is a class IntegerCache takes care of the Integer cache array, which is a private static inner class of Integer class.

There is another interesting example may help us understand this weird situation:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

      Class cache = Integer.class.getDeclaredClasses()[0]; 
      Field myCache = cache.getDeclaredField("cache"); 
      myCache.setAccessible(true);

      Integer[] newCache = (Integer[]) myCache.get(cache); 
      newCache[132] = newCache[133]; 

      Integer a = 2;
      Integer b = a + a;
      System.out.printf("%d + %d = %d", a, a, b); //The output is: 2 + 2 = 5    

}

참고URL : https://stackoverflow.com/questions/3130311/weird-integer-boxing-in-java

반응형