ProgramaciónDesarrollador Senior de Backend en Java

¿Cómo funciona el mecanismo de carga de clases (class loading) en Java, qué tipos de cargadores de clases existen y qué problemas pueden surgir por un uso incorrecto de los mismos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

En Java, las clases se cargan en memoria mediante objetos especiales llamados cargadores de clases (class loaders). Cada cargador de clases forma su propio espacio de visibilidad de clases.

Tipos de cargadores de clases:

  1. Bootstrap ClassLoader — carga las clases base del JDK (rt.jar, etc.).
  2. Extension ClassLoader — carga extensiones desde $JAVA_HOME/lib/ext.
  3. System (Application) ClassLoader — carga clases desde el classpath de la aplicación.
  4. Custom ClassLoader — cargadores personalizados para carga dinámica.

Características:

  • Una clase se considera única dentro de la combinación (cargador de clases, nombre de clase).
  • Dos clases idénticas, cargadas por diferentes cargadores, se consideran tipos diferentes.
  • Los cargadores de clases forman una jerarquía (padre-hijo).
  • Se pueden cargar clases desde bytes (defineClass) — relevante para plugins/JSP, etc.

Pregunta capciosa.

¿Se puede cargar varias versiones de la misma clase en el proceso de la JVM, y cómo se puede hacer esto?

Respuesta: Sí, es posible si se utilizan diferentes cargadores de clases. Las clases con el mismo FQDN, cargadas por diferentes cargadores, se consideran diferentes tipos para la JVM.

Ejemplo:

ClassLoader loader1 = new URLClassLoader(new URL[]{...}); ClassLoader loader2 = new URLClassLoader(new URL[]{...}); Class clazz1 = loader1.loadClass("com.example.MyClass"); Class clazz2 = loader2.loadClass("com.example.MyClass"); System.out.println(clazz1 == clazz2); // false

Ejemplos de errores reales por desconocimiento de las sutilezas del tema.


Historia

En un gran sistema de servidor se usaban plugins, cada uno conectado a través de un cargador de clases personalizado. Los plugins intercambiaban objetos a través de una interfaz común, también cargada por su propio cargador de clases. Se producía un ClassCastException al convertir tipos, ya que la interfaz 'PluginApi' del cargador del plugin y el del sistema principal se consideraban tipos diferentes.


Historia

Un intento de "recarga en caliente" de un servlet a través de un cargador de clases personalizado provocó fugas de memoria: la clase antigua no se descargaba de la memoria, ya que había una referencia en alguna de las variables estáticas. Como resultado, el PermGen se llenaba rápidamente.


Historia

Un producto con soporte para carga dinámica de módulos usaba implícitamente el cargador de clases del sistema, y los desarrolladores accidentalmente reemplazaron su padre, perdiendo acceso a las clases base (por ejemplo, del JDK). Esto se manifestaba en caídas, cuando un nuevo módulo no podía cargar clases estándar de Java, como java.sql.Driver.