¿Hay alguna sobrecarga cuando proyectamos objetos de un tipo a otro? ¿O el compilador simplemente resuelve todo y no hay ningún costo en tiempo de ejecución?
¿Es esto algo general o hay casos diferentes?
Por ejemplo, supongamos que tenemos una matriz de Object [], donde cada elemento puede tener un tipo diferente. Pero siempre sabemos con certeza que, digamos, el elemento 0 es un Double, el elemento 1 es una Cadena. (Sé que este es un diseño incorrecto, pero supongamos que tuve que hacer esto).
¿La información de tipo de Java todavía se mantiene en tiempo de ejecución? O todo se olvida después de la compilación, y si hacemos elementos (Double) [0], simplemente seguiremos el puntero e interpretaremos esos 8 bytes como un doble, ¿lo que sea?
No tengo muy claro cómo se hacen los tipos en Java. Si tiene alguna recomendación sobre libros o artículos, gracias también.
Respuestas:
Hay 2 tipos de casting:
Conversión implícita , cuando se transmite de un tipo a un tipo más amplio, que se realiza automáticamente y no hay gastos generales:
Casting explícito , cuando pasas de un tipo más amplio a uno más estrecho. Para este caso, debe usar explícitamente la transmisión de esa manera:
En este segundo caso, hay una sobrecarga en tiempo de ejecución, porque los dos tipos deben verificarse y, en caso de que la conversión no sea factible, JVM debe lanzar una ClassCastException.
Tomado de JavaWorld: The cost of casting
fuente
((String)o).someMethodOfCastedClass()
Para una implementación razonable de Java:
Cada objeto tiene un encabezado que contiene, entre otras cosas, un puntero al tipo de ejecución (por ejemplo ,
Double
oString
, pero nunca podría serCharSequence
oAbstractList
). Suponiendo que el compilador en tiempo de ejecución (generalmente HotSpot en el caso de Sun) no puede determinar el tipo estáticamente, el código de máquina generado debe realizar algunas comprobaciones.Primero se debe leer ese puntero al tipo de tiempo de ejecución. Esto es necesario para llamar a un método virtual en una situación similar de todos modos.
Para convertir a un tipo de clase, se sabe exactamente cuántas superclases hay hasta que acierta
java.lang.Object
, por lo que el tipo se puede leer con un desplazamiento constante desde el puntero de tipo (en realidad, los primeros ocho en HotSpot). Nuevamente, esto es análogo a leer un puntero de método para un método virtual.Entonces, el valor leído solo necesita una comparación con el tipo estático esperado del elenco. Dependiendo de la arquitectura del conjunto de instrucciones, otra instrucción necesitará bifurcarse (o fallar) en una bifurcación incorrecta. Los ISA como ARM de 32 bits tienen instrucción condicional y pueden hacer que el camino triste pase por el camino feliz.
Las interfaces son más difíciles debido a la herencia múltiple de interfaces. Generalmente, las dos últimas conversiones a interfaces se almacenan en caché en el tipo de tiempo de ejecución. En los primeros días (hace más de una década), las interfaces eran un poco lentas, pero eso ya no es relevante.
Con suerte, puede ver que este tipo de cosas son en gran medida irrelevantes para el rendimiento. Tu código fuente es más importante. En términos de rendimiento, el mayor impacto en su escenario es probable que sean errores de caché al perseguir punteros de objetos por todas partes (la información de tipo, por supuesto, será común).
fuente
El compilador no toma nota de los tipos de elementos individuales de una matriz. Simplemente verifica que el tipo de expresión de cada elemento sea asignable al tipo de elemento de matriz.
Alguna información se mantiene en tiempo de ejecución, pero no los tipos estáticos de los elementos individuales. Puede saberlo mirando el formato de archivo de la clase.
Teóricamente es posible que el compilador JIT pueda usar "análisis de escape" para eliminar verificaciones de tipo innecesarias en algunas asignaciones. Sin embargo, hacer esto en la medida que sugiere estaría más allá de los límites de la optimización realista. La recompensa de analizar los tipos de elementos individuales sería demasiado pequeña.
Además, la gente no debería escribir código de aplicación como ese de todos modos.
fuente
(float) Math.toDegrees(theta)
¿Habrá aquí también una sobrecarga significativa?Se llama a la instrucción de código de bytes para realizar la conversión en tiempo de ejecución
checkcast
. Puede desensamblar el código Java utilizandojavap
para ver qué instrucciones se generan.Para las matrices, Java mantiene la información de tipos en tiempo de ejecución. La mayoría de las veces, el compilador detectará errores de tipo por usted, pero hay casos en los que se encontrará con un
ArrayStoreException
cuando intente almacenar un objeto en una matriz, pero el tipo no coincide (y el compilador no lo detectó) . La especificación del lenguaje Java da el siguiente ejemplo:Point[] pa = cpa
es válido ya queColoredPoint
es una subclase de Point, peropa[0] = new Point()
no es válido.Esto se opone a los tipos genéricos, donde no hay información de tipo guardada en tiempo de ejecución. El compilador inserta
checkcast
instrucciones donde sea necesario.Esta diferencia en la escritura de tipos y matrices genéricos hace que a menudo no sea adecuado mezclar matrices y tipos genéricos.
fuente
En teoría, se introducen gastos generales. Sin embargo, las JVM modernas son inteligentes. Cada implementación es diferente, pero no es descabellado asumir que podría existir una implementación que JIT optimizó las comprobaciones de descarte cuando podría garantizar que nunca habría un conflicto. En cuanto a qué JVM específicas ofrecen esto, no podría decírselo. Debo admitir que me gustaría conocer los detalles de la optimización JIT, pero estos son para que los ingenieros de JVM se preocupen.
La moraleja de la historia es escribir primero un código comprensible. Si experimenta ralentizaciones, perfile e identifique su problema. Hay muchas probabilidades de que no se deba al casting. Nunca sacrifique un código limpio y seguro en un intento de optimizarlo HASTA QUE SEPA QUE LO NECESITA.
fuente