programacion manejo localdatetime horas example ejemplo desde cero java jvm classloader

manejo - programacion en java pdf



¿Dónde comienza realmente la etapa de resolución de la carga de clases de Java? (2)

Acabo de terminar de leer la Especificación de la máquina virtual Java y la sección sobre la carga de clases me dejó perplejo. Por lo que entendí en general y después de leer la especificación, pensé que la creación de instancias general de una clase consistía en los siguientes pasos en el siguiente orden:

  • Creación / carga : el cargador de clases localiza un flujo de bytes que representan la clase, ya sea un archivo o un flujo de red o lo que sea que implemente un cargador de clases para obtener. Si no se puede encontrar una clase, se lanza una ClassNotFoundException . En este punto, ya está ocurriendo una validación básica en la que se lanza un ClassFormatError si la matriz de bytes no representa una clase de Java (por ejemplo, falta el número mágico) o un UnsupportedClassVersionError si la versión de la clase no es compatible con la JVM en ejecución ejemplo.

  • Enlace : La clase está enganchada a la JVM. Si algo sale mal, se lanza una subclase de LinkageError . La vinculación consiste en los tres subpasos:

    • Verificación : se asegura de que la secuencia de bytes represente una clase de Java como, por ejemplo, el código de bytes sin errores formales, como el desbordamiento de pilas de operandos para el código de bytes del método. Si una clase falla la verificación, se lanza un VerifyError .

    • Preparación : la JVM asigna memoria para todos los campos estáticos y puede crear una plantilla de instancia para acelerar la creación de la instancia. Se crean tablas de métodos virtuales. No se lanzan errores específicos de carga de clase en esta etapa. (Sin OutOfMemoryError puede OutOfMemoryError un OutOfMemoryError .)

    • Resolución : todas las referencias simbólicas que ahora se cargaron en el área del método en forma de la agrupación de constantes de tiempo de ejecución se resuelven en los tipos reales cargados por esta JVM. Si se puede resolver una referencia simbólica pero da lugar a un conflicto de definiciones, se lanza un IncompatibleClassChangeError . Si no se puede encontrar una NoClassDefFoundError se hace NoClassDefFoundError se lanza un NoClassDefFoundError que básicamente envuelve una ClassNotFoundException que fue lanzada por el cargador de clases que intenta cargar esta clase a la que se hace referencia. Si una clase referenciada se hace referencia a sí misma, se lanza un ClassCircularityError . La resolución puede ocurrir en uno de los dos sabores que depende de los implementadores de la JVM

      1. Eager : todas las referencias simbólicas a otros campos, métodos o clases se resuelven ahora.

      2. Lazy : la resolución de referencias simbólicas se pospone hasta el primer uso de un método. Esto podría traer consigo que una clase que se refiera a una clase no existente nunca arroje un error si esta referencia nunca necesita ser resuelta.

  • Inicialización : se ejecutan static inicializadores static la clase que se definen en la clase como código Java. Si un inicializador de este tipo provoca una ExceptionInInitializerError , esta excepción se vuelve a generar envuelta en un ExceptionInInitializerError .

Lo que me desconcierta es la fase de resolución del mecanismo de carga de clase anterior. ¿Por qué se define la resolución como un paso explícito dentro de la vinculación que ocurre específicamente después de la preparación ? Ya dentro de la descripción de la fase de carga de clases , se menciona que

Si C tiene superinterfaces directas, las referencias simbólicas de C a sus superinterfaces directas se resuelven usando el algoritmo de §5.4.3.1.

Las referencias simbólicas no se resuelven también mientras se lleva a cabo la verificación, ya que se described verificación:

La verificación (§4.10) garantiza que la representación binaria de una clase o interfaz sea estructuralmente correcta (§4.9). La verificación puede hacer que se carguen clases e interfaces adicionales (§5.3), pero no es necesario que se verifiquen o preparen.

Siempre tengo esta foto en mente

Fuente: http://www.programcreek.com

que he visto en casi cualquier lugar explicando la carga de clases. Si la resolución no se considera más bien como una responsabilidad general que forma parte de todas las fases, creación / carga , verificación , vinculación e inicialización (ya que la resolución se puede hacer perezosamente).

Actualmente, argumentaría que tendría sentido eliminar la etapa de resolución de esta imagen y declararla como un procedimiento general que se puede utilizar en cualquier momento, ya que podría requerirse información sobre otras clases en cualquier etapa, de manera que la carga de tal se requiere clase lo que necesariamente también requiere la resolución de la referencia simbólica a esta clase. A partir de la imagen mostrada, parece que la resolución solo ocurre en un punto específico de una cadena de eventos separados.

Sospecho que esta descripción de la resolución es un paso específico, tal vez solo un legado de un momento en el que la resolución nunca se llevó a cabo de manera perezosa, sino que tenía su lugar donde se resolvieron todas las referencias simbólicas restantes .

Lo que quiero saber : ¿Debería entenderse la resolución en las JVM de hoy como la describí? ¿O me equivoco al respecto y la resolución aún puede entenderse como un paso dedicado en una línea de tiempo fija tal como lo muestra la imagen?


Es difícil de decir, pero creo que solo encontraste una pequeña discrepancia o ambigüedad en la documentación. Los pasos en la documentación no están definidos de manera muy precisa, por lo que la implementación puede ser un poco específica, los pasos pueden superponerse un poco, etc. La principal preocupación en la implementación probablemente fue la velocidad, no la claridad lógica absoluta.

Intente buscar en el código fuente de OpenJDK y podría encontrar algo interesante.


La imagen muestra que la resolución siempre aparece después de la preparación, pero eso no funcionará. Las súper clases directas son necesarias para la preparación, ya que necesita conocimiento sobre los campos de instancia de las súper clases para determinar el diseño de memoria de la instancia del objeto para una clase en particular. Además, los inicializadores estáticos de una clase y sus súper clases deben haberse ejecutado antes de poder usar una clase, es decir, antes de crear instancias o antes de invocar métodos estáticos.

Esto difiere de la resolución de todos los otros tipos de referencia que pueden diferirse mucho más tiempo. Se permite resolver un tipo utilizado en un método justo antes de que se invoque el método la primera vez.

Cuando nos fijamos en el principio del Capítulo 5.4.3. Resolución , se establece explícitamente:

Las instrucciones de la máquina virtual de Java son una nueva opción , checkcast , getfield , getstatic , instanceof , invokedynamic , invokeinterface , invokepecial , invokestatic , invokevirtual , ldc , ldc _w, multianewarray , new , putfield y putstatic hacen referencias simbólicas al conjunto de constantes en tiempo de ejecución. La ejecución de cualquiera de estas instrucciones requiere la resolución de su referencia simbólica.

Así que la diferencia queda bastante clara. Existe la resolución de la superclase directa y las interfaces implementadas directamente (o súper interfaces en el caso de una interfaz), lo que sucede antes y existe la resolución de las referencias simbólicas a los efectos de las instrucciones de código de bytes anteriores que se pueden posponer.