¿Por qué valores de almacenamiento en caché de clases enteras en el rango de -128 a 127?

81

Con respecto a mi pregunta anterior, ¿por qué == las comparaciones con Integer.valueOf (String) dan resultados diferentes para 127 y 128? , sabemos que Integer classtiene un caché que almacena valores entre -128y 127.

Me pregunto, ¿por qué entre -128 y 127 ?

La documentación de Integer.valueOf () indica que " almacena en caché los valores solicitados con frecuencia " . Pero, ¿los valores entre -128y 127se solicitan con frecuencia de verdad? Pensé que los valores solicitados con frecuencia son muy subjetivos.
¿Hay alguna posible razón detrás de esto?

De la documentación también se indica: "... y puede almacenar en caché otros valores fuera de este rango " .
¿Cómo se puede lograr esto?

DnR
fuente
7
Con respecto a la documentación: Oracle solo está cubriendo sus traseros en caso de que decidan cambiar el comportamiento más adelante. Por ejemplo, pueden decidir que Java 9 almacenará en caché de -1024 a 1023. El mensaje es, no confíe en que la caché contenga o no contenga un entero específico.
Dawood ibn Kareem
7
Supongo que se repite mucho más a menudo de 0 a X que de 13476 a Y. Deben haber decidido que los valores negativos también deberían incluirse y -128 -> 127 tiene sentido para un byte con signo.
Jeroen Vannevel
2
¿No se repite casi siempre con ints primitivos, no enteros en caja? El almacenamiento en caché no se aplica.
bradvido
2
La caché es puramente una cuestión de rendimiento. Siempre que no le esté creando un problema de rendimiento, no debería importarle qué rango se almacena en caché. (Sería el colmo de la locura incorporar en su código una dependencia del almacenamiento en caché de Integer.)
Hot Licks
3
@JohnR está en la especificación del lenguaje Java, vea la respuesta de Assylias a continuación.
Zac Thompson

Respuestas:

105

Me pregunto, ¿por qué entre -128 y 127?

Se puede almacenar en caché un rango mayor de enteros , pero al menos aquellos entre -128 y 127 deben almacenarse en caché porque es un mandato de la Especificación del lenguaje Java (el énfasis es mío):

Si el valor p en el cuadro es verdadero, falso, un byte o un carácter en el rango \ u0000 a \ u007f, o un número int o corto entre -128 y 127 (inclusive) , entonces sean r1 y r2 los resultados de dos conversiones de boxeo cualesquiera de p. Siempre es el caso de que r1 == r2.

El fundamento de este requisito se explica en el mismo párrafo:

Idealmente, encuadrar un valor primitivo p dado siempre produciría una referencia idéntica . En la práctica, esto puede no ser factible utilizando las técnicas de implementación existentes. Las reglas anteriores son un compromiso pragmático. La cláusula final anterior requiere que ciertos valores comunes siempre estén encuadrados en objetos indistinguibles. [...]

Esto asegura que, en los casos más comunes, el comportamiento será el deseado, sin imponer una penalización indebida del rendimiento, especialmente en dispositivos pequeños . Las implementaciones con menos memoria limitada pueden, por ejemplo, almacenar en caché todos los valores char y short, así como los valores int y long en el rango de -32K a + 32K.


¿Cómo puedo almacenar en caché otros valores fuera de este rango?

Puede utilizar la -XX:AutoBoxCacheMaxopción JVM, que no está realmente documentada en la lista de opciones de JVM de Hotspot disponibles . Sin embargo, se menciona en los comentarios dentro de la Integerclase alrededor de la línea 590 :

El tamaño de la caché puede controlarse mediante la -XX:AutoBoxCacheMax=<size>opción.

Tenga en cuenta que esto es específico de la implementación y puede o no estar disponible en otras JVM.

Assylias
fuente
2
Esta es la mejor y completa respuesta: la pregunta confunde el rango de -128 a 127 con los "valores solicitados con frecuencia", cuando en realidad son por diferentes razones. -128 a 127 se almacenan en caché para boxeo. Los "valores solicitados con frecuencia" se almacenan en caché para mejorar el rendimiento.
Zac Thompson
@ZacThompson, gracias por señalar esto. Mi comentario anterior no fue correcto. La frase clave de la especificación es "un int ... entre -128 y 127 (inclusive), entonces sean r1 y r2 los resultados de dos conversiones de boxeo cualesquiera de p. Siempre es el caso de que r1 == r2". Entonces, si entiendo correctamente, la especificación exige que Integer.valueOf (X) == Integer.valueOf (X) donde -128 <= X <= 127.
John R
Esta es la única respuesta al "por qué", parte de la pregunta que ofrece algo más que "es el predeterminado". Sin embargo, esta respuesta no está completa porque no aborda la parte "cómo" de la pregunta. Hacer referencia a las respuestas de otros en XX: AutoBoxCacheMax y agregar información sobre cómo controlar el comportamiento de almacenamiento en caché en otras implementaciones de JVM (o indicar qué implementaciones de JVM tienen opciones para controlar este comportamiento) haría que esta sea una respuesta completa.
John R
"En la práctica, esto puede no ser factible utilizando las técnicas de implementación existentes". No puedo conseguir esta línea. ¿Puedes explicarlo?
niiraj874u
2
@ niiraj874u La implementación actual usa un caché que reside en la memoria - cada entero "canónico" se guarda en ese caché. Por lo tanto, almacenar en caché todos los números enteros significaría que es posible que tenga que almacenar hasta 2 ^ 32 números enteros (= 15+ GB) en la memoria, lo cual no es razonable, incluso en una computadora de escritorio moderna.
Assylias
22

-128 a 127 es el tamaño predeterminado. Pero javadoc también dice que el tamaño de la caché de Integer puede ser controlado por la -XX:AutoBoxCacheMax=<size>opción. Tenga en cuenta que establece solo un valor alto, el valor bajo siempre es -128. Esta característica se introdujo en 1.6.

En cuanto a por qué -128 a 127, este es un rango de valores de bytes y es natural usarlo para una caché muy pequeña.

Evgeniy Dorofeev
fuente
¿Cómo podemos implementar el -XX:AutoBoxCacheMax=<size>?
DnR
ejecute java -XX: AutoBoxCacheMax = 256 ... y verá que Integer.valueOf (256) == Integer.valueOf (256)
Evgeniy Dorofeev
al ejecutar java -XX:AutoBoxCacheMax=256en la consola, obtuveError:could not create the Java Virtual Machine
DnR
tratar java -version debe ser 1.6 o superior, mi 1.7 funciona BIEN
Evgeniy Dorofeev
2
Derecha, esta es la razón por javadoc dice ..may ser controlado ... mi Java es de 64 bits
Evgeniy Dorofeev
5

La razón para almacenar en caché los números enteros pequeños, si eso es lo que está preguntando, es que muchos algoritmos utilizan números enteros pequeños en sus cálculos, por lo que suele valer la pena evitar la sobrecarga de creación de objetos para estos valores.

La pregunta entonces es qué enteros almacenar en caché. Una vez más, hablando en general, la frecuencia con la que se usan los valores constantes tiende a disminuir a medida que aumenta el valor absoluto de la constante: todos pasan mucho tiempo usando los valores 1, 2 o 10, relativamente pocos usan el valor 109 muy intensamente menos tendrán rendimiento dependiendo de la rapidez con la que se pueda obtener un entero para 722 .. Java eligió asignar 256 ranuras que abarcan el rango de un valor de byte firmado. Esta decisión puede haberse basado en el análisis de programas existentes en ese momento, pero es igualmente probable que haya sido puramente arbitraria. Es una cantidad razonable de espacio para invertir, se puede acceder rápidamente (máscara para averiguar si el valor está en el rango de la caché, luego una búsqueda rápida en la tabla para acceder a la caché), y definitivamente cubrirá los casos más comunes.

En otras palabras, creo que la respuesta a su pregunta es "no es tan subjetivo como pensaba, pero los límites exactos son en gran medida una decisión práctica ... y la evidencia experimental ha sido que fue lo suficientemente bueno. "

keshlam
fuente
3

El valor entero máximo que se puede almacenar en caché se puede configurar a través de la propiedad del sistema, es decir java.lang.Integer.IntegerCache.high( -XX:AutoBoxCacheMax). La caché se implementa mediante una matriz.

    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() {}
}
la hora del examen
fuente
0

Cuando se encuentra con la clase Integer y siempre en un cuadro dentro del rango -128 a 127, siempre es mejor convertir el objeto Integer en un valor int como se muestra a continuación.

<Your Integer Object>.intValue()
Teja
fuente