¿El casting de Java introduce sobrecarga? ¿Por qué?

¿Hay alguna sobrecarga cuando lanzamos objetos de un tipo a otro? ¿O el comstackdor simplemente resuelve todo y no hay ningún costo en el tiempo de ejecución?

¿Es esto algo general, o hay diferentes casos?

Por ejemplo, supongamos que tenemos una matriz de Objeto [], donde cada elemento podría tener un tipo diferente. Pero siempre sabemos con certeza que, por ejemplo, el elemento 0 es un doble, el elemento 1 es una cadena. (Sé que este es un diseño incorrecto, pero supongamos que tengo que hacer esto).

¿La información de tipo de Java todavía se mantiene en el tiempo de ejecución? O todo se olvida después de la comstackción, y si hacemos (Doble) elementos [0], simplemente seguiremos el puntero e interpretaremos esos 8 bytes como un doble, cualquiera que sea eso?

No tengo muy claro cómo se hacen los tipos en Java. Si tiene alguna recomendación sobre libros o artículos, entonces gracias también.

    Hay 2 tipos de casting:

    Conversión implícita , cuando seleccionas de un tipo a un tipo más amplio, lo que se hace automáticamente y no hay gastos generales:

    String s = "Cast"; Object o = s; // implicit casting 

    Transmisión explícita , cuando pasas de un tipo más amplio a uno más estrecho. Para este caso, debes usar explícitamente el casting así:

     Object o = someObject; String s = (String) o; // explicit casting 

    En este segundo caso, hay una sobrecarga en tiempo de ejecución, porque los dos tipos deben verificarse y, en caso de que dicha conversión no sea factible, JVM debe lanzar una ClassCastException.

    Tomado de JavaWorld: El costo del casting

    La conversión se utiliza para convertir entre tipos, entre tipos de referencia en particular, para el tipo de operación de conversión en la que estamos interesados ​​aquí.

    Las operaciones de actualización (también llamadas conversiones de ampliación en la especificación de lenguaje de Java) convierten una referencia de subclase en una referencia de clase antecesora. Esta operación de conversión es normalmente automática, ya que siempre es segura y puede ser implementada directamente por el comstackdor.

    Las operaciones descendentes (también llamadas conversiones de angostamiento en la especificación del lenguaje Java) convierten una referencia de clase antecesora a una referencia de subclase. Esta operación de conversión crea una sobrecarga de ejecución, ya que Java requiere que se verifique el lanzamiento en tiempo de ejecución para asegurarse de que sea válido. Si el objeto al que se hace referencia no es una instancia del tipo de destino para el reparto o una subclase de ese tipo, el bash de lanzamiento no está permitido y debe arrojar una java.lang.ClassCastException.

    Para una implementación razonable de Java:

    Cada objeto tiene un encabezado que contiene, entre otras cosas, un puntero al tipo de tiempo de ejecución (por ejemplo, Double or String , pero nunca podría ser CharSequence o AbstractList ). Suponiendo que el comstackdor de tiempo de ejecución (generalmente HotSpot en el caso de Sun) no puede determinar el tipo de forma estática, el código de máquina generado debe realizar algunas comprobaciones.

    En primer lugar, debe leerse el 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 transmitir a un tipo de clase, se sabe exactamente cuántas superclases hay hasta que 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). De nuevo, esto es análogo a leer un puntero de método para un método virtual.

    Entonces, el valor de lectura solo necesita una comparación con el tipo estático esperado del elenco. Dependiendo de la architecture del conjunto de instrucciones, otra instrucción necesitará derivarse (o fallar) en una twig incorrecta. Las ISA como ARM de 32 bits tienen instrucciones condicionales y pueden hacer que la ruta triste pase por la ruta feliz.

    Las interfaces son más difíciles debido a la herencia múltiple de la interfaz. En general, los últimos dos moldes a las 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, se puede ver que este tipo de cosas es en gran medida irrelevante para el rendimiento. Tu código fuente es más importante. En términos de rendimiento, es probable que el mayor golpe en su escenario sea la falta de caché al perseguir punteros de objeto por todas partes (la información de tipo será, por supuesto, común).

    Por ejemplo, supongamos que tenemos una matriz de Objeto [], donde cada elemento podría tener un tipo diferente. Pero siempre sabemos con certeza que, por ejemplo, el elemento 0 es un doble, el elemento 1 es una cadena. (Sé que este es un diseño incorrecto, pero supongamos que tengo que hacer esto).

    El comstackdor no nota los tipos de los elementos individuales de una matriz. Simplemente comprueba que el tipo de expresión de cada elemento se puede asignar al tipo de elemento de matriz.

    ¿La información de tipo de Java todavía se mantiene en el tiempo de ejecución? O todo se olvida después de la comstackción, y si hacemos (Doble) elementos [0], simplemente seguiremos el puntero e interpretaremos esos 8 bytes como un doble, cualquiera que sea eso?

    Parte de la información se mantiene durante el tiempo de ejecución, pero no los tipos estáticos de los elementos individuales. Puedes ver esto al mirar el formato de archivo de la clase.

    Es teóricamente posible que el comstackdor JIT pueda usar el “análisis de escape” para eliminar verificaciones de tipos innecesarias en algunas asignaciones. Sin embargo, hacer esto en la medida en que sugiera 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, las personas no deberían escribir código de aplicación así de todos modos.

    La instrucción de código de bytes para realizar el casting en el tiempo de ejecución se llama checkcast . Puede desensamblar código Java usando javap para ver qué instrucciones se generan.

    Para las matrices, Java mantiene la información del tipo en tiempo de ejecución. La mayoría de las veces, el comstackdor detectará errores de tipo para usted, pero hay casos en los que se encontrará con una ArrayStoreException al tratar de almacenar un objeto en una matriz, pero el tipo no coincide (y el comstackdor no lo atrapó) ) La especificación del lenguaje Java da el siguiente ejemplo:

     class Point { int x, y; } class ColoredPoint extends Point { int color; } class Test { public static void main(String[] args) { ColoredPoint[] cpa = new ColoredPoint[10]; Point[] pa = cpa; System.out.println(pa[1] == null); try { pa[0] = new Point(); } catch (ArrayStoreException e) { System.out.println(e); } } } 

    Point[] pa = cpa es válido porque ColoredPoint es una subclase de Point, pero pa[0] = new Point() no es válido.

    Esto se opone a los tipos generics, donde no hay información de tipo guardada en tiempo de ejecución. El comstackdor inserta instrucciones de checkcast cuando sea necesario.

    Esta diferencia de tipeo para tipos generics y matrices hace que a menudo no sea adecuado mezclar matrices y tipos generics.

    En teoría, se introducen gastos generales. Sin embargo, las JVM modernas son inteligentes. Cada implementación es diferente, pero no es irrazonable suponer que podría existir una implementación que JIT optimizara las verificaciones de casting de distancia cuando podría garantizar que nunca habría un conflicto. En cuanto a las JVM específicas que ofrecen esto, no podría decírtelo. Debo admitir que me gustaría conocer los detalles de la optimización de JIT yo mismo, pero estos son para los ingenieros de JVM que preocuparse.

    La moraleja de la historia es escribir primero un código comprensible. Si experimentas ralentizaciones, perfila e identifica tu problema. Las probabilidades son buenas de que no se deba a un casting. Nunca sacrifique el código limpio y seguro en un bash de optimizarlo HASTA QUE SEPA QUE NECESITA.