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
o String
, pero nunca podría ser CharSequence
o AbstractList
). 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).