Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Orígenes e Historia de Java

Universidad Nacional de Rio Negro - Sede Andina

Este capítulo presenta el contexto histórico y técnico que dio origen a Java. Comprender estos orígenes ayuda a entender las decisiones de diseño del lenguaje y por qué funciona como funciona.

El Nacimiento de Java: El Green Project

El contexto: Sun Microsystems y los años 90

Sun Microsystems fue una empresa estadounidense fundada en 1982, conocida por fabricar estaciones de trabajo (computadoras de alto rendimiento para profesionales) y servidores. El nombre “SUN” era un acrónimo de “Stanford University Network”, ya que sus fundadores eran estudiantes de Stanford.

A principios de la década de 1990, Sun anticipó que la próxima gran ola tecnológica no vendría de las estaciones de trabajo tradicionales, sino de la electrónica de consumo: televisores inteligentes, microondas programables, controles remotos avanzados y otros dispositivos del hogar que comenzaban a incluir pequeños procesadores.

Wikipedia: Sun Microsystems

El Green Team y el proyecto secreto

En 1991, James Gosling (un ingeniero canadiense reconocido por crear el editor de texto Emacs para Unix) lideró un equipo de ingenieros de élite conocido como el “Green Team”. Este equipo operó en secreto durante 18 meses, trabajando en un edificio apartado de las oficinas principales de Sun —casi como una startup interna— lo que les permitió tomar decisiones de diseño radicales sin las restricciones burocráticas típicas de una gran corporación.

El objetivo principal era desarrollar un sistema operativo y un lenguaje de programación para dispositivos inteligentes con recursos limitados (poca memoria, procesadores lentos). El resultado de este esfuerzo fue el Star7 (*7), un prototipo de dispositivo portátil con:

Este dispositivo era, esencialmente, un PDA (Personal Digital Assistant) —precursor de los smartphones actuales— creado más de una década antes del iPhone.

El Green Team: Los Arquitectos Originales

El equipo que dio vida a Java estaba compuesto por ingenieros excepcionales, cada uno con contribuciones específicas:

Table 1:Miembros clave del Green Team

IngenieroRol PrincipalContribución Notable
James GoslingArquitecto del lenguajeDiseño del compilador y la sintaxis de Oak/Java
Mike SheridanGerente de proyectoCoordinación y visión de negocio
Patrick NaughtonInterfaz gráficaCreación de Duke y el sistema de ventanas
Chris WarthCompiladorOptimización del bytecode
Ed FrankHardwareDiseño del dispositivo Star7
Jonathan PayneHerramientasDesarrollo del primer navegador con soporte Java (HotJava)

¿Por qué necesitaban un nuevo lenguaje?

Para entender por qué el Green Team no usó un lenguaje existente, es necesario comprender el panorama de la programación en 1991.

El Desafío Tecnológico frente a C/C++

En aquella época, el desarrollo de software estaba dominado por C y C++. Para entender qué significan los problemas que estos lenguajes presentaban, es necesario explicar algunos conceptos:

¿Qué es la gestión manual de memoria?

Cuando un programa necesita almacenar datos (por ejemplo, una lista de números o el texto de un documento), debe pedirle al sistema operativo un espacio en la memoria RAM. En C/C++, el programador es responsable de:

  1. Reservar memoria usando funciones como malloc() (memory allocation)

  2. Usar esa memoria para almacenar datos

  3. Liberar la memoria con free() cuando ya no la necesita

Si el programador olvida liberar la memoria, el programa consume cada vez más RAM hasta agotar los recursos del sistema (esto se llama memory leak o fuga de memoria). Si libera la memoria, pero luego intenta usarla, el programa falla de forma impredecible (dangling pointer o puntero colgante).

¿Qué son los punteros?

Un puntero es una variable que almacena una dirección de memoria en lugar de un valor directo. Pensá en los punteros como “direcciones postales” que indican dónde encontrar los datos reales. En C/C++, los punteros permiten:

Esta flexibilidad es poderosa, pero peligrosa: un error de un byte en la dirección puede hacer que el programa lea o escriba en el lugar equivocado, corrompiendo datos o causando que el programa se cierre abruptamente.

¿Qué es un buffer overflow?

Un buffer es un espacio de memoria reservado para almacenar datos temporalmente (como un arreglo de caracteres para guardar texto). Un buffer overflow (desbordamiento de búfer) ocurre cuando un programa escribe más datos de los que caben en el buffer, sobrescribiendo la memoria adyacente.

Este error no solo causa fallos; es una de las vulnerabilidades de seguridad más explotadas en la historia de la informática. Un atacante puede diseñar datos especiales que, al desbordar el buffer, sobrescriban instrucciones del programa y ejecuten código malicioso.

Los problemas concretos de C/C++ para dispositivos embebidos

Para el Green Team, estos problemas eran críticos:

Java nació con la misión de eliminar estas deficiencias a través de una arquitectura basada en abstracción: el lenguaje escondería los detalles peligrosos del hardware detrás de una capa segura.

Los Cinco Principios de Diseño Originales

El equipo de Gosling estableció cinco objetivos fundamentales que guiaron el diseño del lenguaje. Cada decisión del lenguaje puede rastrearse a uno de estos principios:

  1. Simple, orientado a objetos y familiar

    Debía ser fácil de aprender para programadores de C/C++, manteniendo una sintaxis similar (llaves, punto y coma, tipos de datos), pero eliminando las complejidades innecesarias:

    • Sin herencia múltiple de clases: En C++, una clase puede heredar de múltiples clases padres, lo que genera conflictos cuando dos padres definen el mismo método (el “problema del diamante”). Java permite implementar múltiples interfaces, pero solo heredar de una clase.

    • Sin aritmética de punteros: No se puede sumar números a direcciones de memoria para “navegar” arbitrariamente.

    • Sin preprocesador: C usa directivas como #define y #include que el preprocesador ejecuta antes de compilar, generando código difícil de depurar. Java eliminó este paso.

  2. Robusto y seguro

    El lenguaje debía prevenir errores comunes:

    • Verificación de tipos estricta: El compilador rechaza operaciones entre tipos incompatibles.

    • Verificación de límites de arreglos: Acceder al elemento 10 de un arreglo de 5 elementos lanza una excepción en lugar de corromper memoria.

    • Sin conversiones implícitas peligrosas: Convertir un número de punto flotante a entero requiere un cast explícito.

  3. Arquitectura neutral y portable

    El mismo programa debía ejecutarse sin modificaciones en cualquier hardware. Esto se logró mediante el bytecode y la JVM (explicados más adelante).

  4. Alto rendimiento

    A pesar de la capa de abstracción, el rendimiento debía ser aceptable para aplicaciones reales. El compilador JIT (Just-In-Time) fue la solución: traducir el código más usado a instrucciones nativas del procesador mientras el programa se ejecuta.

  5. Interpretado, multihilo y dinámico

    • Interpretado: El bytecode se ejecuta sin necesidad de compilación separada para cada plataforma.

    • Multihilo (multithreaded): Soporte nativo para ejecutar múltiples tareas simultáneamente (hilos o threads), algo que en C requería bibliotecas específicas del sistema operativo.

    • Dinámico: Las clases se cargan cuando se necesitan, no todas al inicio del programa.

La Máquina Virtual de Java (JVM) y el Bytecode

La innovación fundamental de Java no fue solo el lenguaje, sino la creación de una Máquina Virtual (JVM) — una computadora simulada por software que ejecuta los programas Java.

¿Qué es una máquina virtual?

Una máquina virtual es un programa que simula ser una computadora completa. Así como un emulador de videojuegos simula una consola antigua en tu PC moderna, la JVM simula una computadora especial diseñada específicamente para ejecutar programas Java.

Esta “computadora virtual” tiene:

La diferencia crucial con C++ es la siguiente:

¿Qué es el bytecode?

El bytecode es un conjunto de instrucciones diseñadas para una máquina virtual, no para un procesador real. Cada instrucción ocupa uno o más bytes (de ahí el nombre “byte-code”).

Por ejemplo, el bytecode de Java incluye instrucciones como:

Estas instrucciones son más simples y abstractas que las de un procesador real, lo que las hace portables pero también más lentas de ejecutar directamente.

¿Cómo funciona el flujo WORA?

WORA significa “Write Once, Run Anywhere” (Escribilo una vez, ejecutalo en cualquier lugar). Este es el proceso completo:

  1. Escritura: El programador escribe código fuente en archivos .java usando cualquier editor de texto.

  2. Compilación a Bytecode: El compilador javac (Java Compiler) transforma el código fuente en bytecode, guardado en archivos .class. Este bytecode es idéntico sin importar en qué sistema operativo se compiló.

  3. Distribución: Los archivos .class (o empaquetados en archivos .jar) se distribuyen a los usuarios.

  4. Ejecución: En la computadora del usuario, la JVM lee el bytecode. La JVM existe en versiones específicas para cada combinación de sistema operativo y procesador (Windows/Intel, macOS/ARM, Linux/x86, etc.), pero todas entienden el mismo bytecode.

  5. Compilación JIT: Para maximizar el rendimiento, la JVM incluye un compilador Just-In-Time (JIT) que identifica las partes del código que se ejecutan frecuentemente (“puntos calientes” o hotspots) y las traduce a código máquina nativo del procesador. Esto ocurre mientras el programa se ejecuta, de forma transparente.

Arquitectura de la JVM: El código fuente se compila a bytecode, un formato universal que la JVM de cada plataforma puede ejecutar.

Figure 1:Arquitectura de la JVM: El código fuente se compila a bytecode, un formato universal que la JVM de cada plataforma puede ejecutar.

Estructura Interna de la JVM

La JVM no es un componente monolítico; está organizada en subsistemas especializados que trabajan juntos:

Class Loader (Cargador de Clases)

Cuando un programa Java necesita usar una clase (por ejemplo, String o una clase que vos escribiste), el Class Loader la busca y la carga en memoria. Las clases se cargan bajo demanda: si tu programa nunca usa la clase ArrayList, nunca se carga.

El Class Loader tiene una jerarquía de tres niveles:

  1. Bootstrap ClassLoader: Carga las clases fundamentales de Java (java.lang.String, java.lang.Object, etc.) desde el JDK.

  2. Extension ClassLoader: Carga extensiones del JDK.

  3. Application ClassLoader: Carga las clases de tu aplicación.

Verificador de Bytecode

Antes de ejecutar cualquier clase cargada, la JVM analiza su bytecode para verificar que:

Esta verificación es crucial para la seguridad: previene que código malicioso manipule la JVM.

Intérprete

El intérprete lee el bytecode instrucción por instrucción y ejecuta la acción correspondiente. Es la forma más simple de ejecución, pero también la más lenta.

Compilador JIT (Just-In-Time)

El compilador JIT observa qué partes del código se ejecutan repetidamente y las compila a código máquina nativo del procesador. Esto significa que:

El JIT realiza optimizaciones sofisticadas:

Garbage Collector (Recolector de Basura)

El Garbage Collector (GC) gestiona automáticamente la memoria. Cuando creás un objeto con new, la JVM reserva espacio en el heap (montículo). Cuando ese objeto ya no es accesible (ninguna variable lo referencia), el GC lo identifica y libera su memoria.

El GC funciona en segundo plano, pausando brevemente el programa para realizar la limpieza. Las JVMs modernas tienen algoritmos sofisticados que minimizan estas pausas:

Áreas de Memoria

La JVM organiza la memoria en regiones específicas:

El Modelo de Seguridad: El Sandbox

Uno de los aspectos más innovadores de Java fue su modelo de seguridad en capas, diseñado originalmente para proteger a los usuarios de código potencialmente malicioso descargado de internet.

¿Qué es un Sandbox?

Un sandbox (caja de arena) es un entorno de ejecución restringido donde el código puede ejecutarse sin poder dañar el sistema que lo hospeda. Imaginá un niño jugando en una caja de arena: puede hacer lo que quiera dentro de la caja, pero no puede salir de ella para causar problemas en el resto del parque.

En Java, el sandbox original permitía que los Applets (pequeños programas Java embebidos en páginas web) se ejecutaran en el navegador del usuario sin poder:

El modelo de seguridad de Java: múltiples capas de verificación protegen el sistema del código no confiable.

Figure 2:El modelo de seguridad de Java: múltiples capas de verificación protegen el sistema del código no confiable.

Las capas del modelo de seguridad

  1. Verificación de Bytecode:

    Antes de ejecutar cualquier clase, la JVM verifica que el bytecode sea estructuralmente correcto. Esto previene que un atacante modifique manualmente un archivo .class para incluir instrucciones ilegales que exploten vulnerabilidades.

  2. Security Manager (Gestor de Seguridad):

    Un componente configurable que intercepta operaciones sensibles y decide si permitirlas. Por ejemplo, antes de que el código intente abrir un archivo, el Security Manager verifica si tiene permiso. Las aplicaciones pueden definir políticas de seguridad que especifican qué permisos tiene cada porción de código.

  3. Class Loader jerárquico:

    Separa el código confiable del no confiable mediante diferentes cargadores de clases. Las clases del sistema (cargadas por el Bootstrap ClassLoader) tienen todos los privilegios; las clases descargadas de internet tienen privilegios restringidos.

El Salto a la Web (1995)

El fracaso comercial inicial

Hacia 1994, el mercado de dispositivos inteligentes aún no estaba maduro. El Green Project intentó vender su tecnología a empresas de electrónica de consumo, pero ninguna mostró interés. El proyecto enfrentaba la cancelación.

La oportunidad de la World Wide Web

Sin embargo, el surgimiento explosivo de la World Wide Web ofreció una oportunidad inesperada. La web de 1994-1995 era muy diferente a la actual:

Sun Microsystems se dio cuenta de que la red necesitaba precisamente lo que Java ofrecía: un lenguaje seguro (para que los usuarios confiaran en ejecutar código descargado) e independiente de la plataforma (ya que los usuarios tenían Windows, Mac y Unix).

El lanzamiento y los Applets

El lanzamiento oficial de Java en mayo de 1995 incluyó los Applets, pequeños programas Java que se descargaban automáticamente cuando un usuario visitaba una página web y se ejecutaban dentro del navegador.

Por primera vez, las páginas web podían tener:

La demostración más famosa fue una animación de moléculas en 3D que los usuarios podían rotar con el mouse —algo trivial hoy, pero revolucionario en 1995.

¿Qué son exactamente los Applets?

Un Applet era una clase Java que:

  1. Se almacenaba en un servidor web

  2. Se descargaba al navegador cuando el usuario visitaba la página

  3. Se ejecutaba dentro de un área rectangular de la página web

  4. Tenía acceso limitado al sistema del usuario (sandbox)

El HTML para incluir un applet era:

<applet code="MiApplet.class" width="300" height="200">
  Tu navegador no soporta applets.
</applet>

Competidores y el Contexto Tecnológico de los 90

Java no surgió en el vacío. Durante la década de 1990, varias tecnologías competían por dominar el desarrollo de software:

Table 2:Tecnologías competidoras de Java en 1995

TecnologíaFortalezasDebilidades frente a Java
C++Máximo rendimiento, control total del hardwareComplejidad extrema, errores de memoria frecuentes, cada plataforma requiere recompilación
Visual BasicMuy fácil de aprender, desarrollo rápido de interfaces gráficasSolo funcionaba en Windows, rendimiento limitado para aplicaciones grandes
SmalltalkOrientación a objetos pura (todo es un objeto), entorno de desarrollo interactivoRendimiento inferior, ecosistema pequeño, sintaxis muy diferente a C
DelphiAlta productividad, compilación a código nativo velozSolo Windows, licencia propietaria costosa
ActiveX/COMIntegración total con Windows, podía usar cualquier lenguajeSolo Windows, graves problemas de seguridad (acceso completo al sistema)

¿Qué es ActiveX y por qué era peligroso?

ActiveX fue la respuesta de Microsoft a los applets de Java. Los controles ActiveX eran componentes de software que podían embeberse en páginas web, pero con una diferencia crucial: tenían acceso completo al sistema del usuario.

Mientras un applet Java solo podía dibujar en su rectángulo asignado, un control ActiveX podía leer cualquier archivo del disco, instalar software, modificar el registro de Windows, o hacer cualquier cosa que el usuario pudiera hacer.

Microsoft intentó mitigar esto con un sistema de “firma digital”: los desarrolladores podían firmar sus controles, y el navegador preguntaba al usuario si confiaba en ese desarrollador. Pero esto dependía de que:

  1. Los usuarios leyeran y entendieran las advertencias (la mayoría hacía clic en “Sí” sin leer)

  2. Los desarrolladores legítimos no cometieran errores de seguridad

  3. Nadie robara certificados de firma

Los tres supuestos resultaron falsos, y ActiveX se convirtió en uno de los vectores de ataque más explotados de la historia de la computación.

La ventaja competitiva de Java fue combinar la potencia de un lenguaje serio con la seguridad del sandbox que ActiveX no ofrecía.

La Guerra de los Navegadores y Microsoft

El éxito de Java en la web provocó una respuesta agresiva de Microsoft. En 1996, Microsoft licenció Java de Sun y creó su propia implementación: Microsoft J++ (Java plus plus).

El problema: extensiones propietarias

Microsoft modificó su JVM para incluir extensiones propietarias que solo funcionaban en Windows:

Un programa Java escrito usando estas extensiones funcionaba perfectamente en Windows, pero fallaba en cualquier otro sistema operativo. Esto violaba directamente la filosofía WORA y fragmentaba el ecosistema.

Sun demandó a Microsoft en 1997, argumentando que Microsoft había incumplido los términos de la licencia al crear una versión incompatible de Java. El resultado del conflicto legal fue:

  1. Microsoft perdió y debió pagar más de 20 millones de dólares

  2. Microsoft abandonó J++ y dejó de incluir la JVM en Windows

  3. Microsoft creó C# y .NET como respuesta directa a Java —un ecosistema propio sobre el que tendrían control total

Evolución y la Era de Oracle

De Sun Microsystems a Oracle (2010)

Sun Microsystems, a pesar de haber creado Java y otras tecnologías influyentes (como el sistema de archivos NFS y el sistema operativo Solaris), enfrentó dificultades financieras durante la década de 2000. En 2010, Oracle Corporation —una empresa especializada en bases de datos empresariales— adquirió Sun por 7.400 millones de dólares.

Esta transición generó incertidumbre en la comunidad Java:

La demanda Oracle vs Google

Los temores se materializaron parcialmente cuando Oracle demandó a Google en 2010, alegando que Android violaba patentes y derechos de autor de Java. El caso llegó hasta la Corte Suprema de Estados Unidos, que en 2021 falló a favor de Google, estableciendo que el uso de APIs de Java en Android constituía “uso justo” (fair use).

El lado positivo: desarrollo acelerado

Bajo el mando de Oracle, Java adoptó un ritmo de innovación más acelerado que en los últimos años de Sun:

Jakarta EE: El ecosistema empresarial

Java EE (Enterprise Edition) era el conjunto de especificaciones para aplicaciones empresariales: servidores web, bases de datos, mensajería, etc. En 2017, Oracle transfirió Java EE a la Eclipse Foundation, una organización sin fines de lucro.

Debido a restricciones de marca (Oracle mantiene los derechos sobre el nombre “Java”), el proyecto fue renombrado Jakarta EE. Esto consolidó un ecosistema de código abierto donde la comunidad —no una sola empresa— controla el futuro de las especificaciones empresariales.

El Ciclo Moderno de Lanzamientos

El problema del ciclo antiguo

Históricamente, las versiones de Java se espaciaban por años:

Este ritmo lento significaba que las nuevas características tardaban años en llegar a los desarrolladores, mientras que lenguajes más nuevos (como Kotlin, Swift o Go) evolucionaban rápidamente.

El nuevo modelo: lanzamientos cada 6 meses

A partir de Java 10 (2018), Oracle adoptó un ciclo de lanzamientos cada seis meses:

¿Qué es LTS (Long Term Support)?

No todas las versiones son iguales:

Las versiones LTS actuales son:

Más allá del soporte principal (Premier Support), las versiones LTS cuentan con opciones de Soporte Extendido comercial. Proveedores como Oracle, Azul Systems o Red Hat ofrecen contratos de nivel de servicio (SLA) que garantizan actualizaciones de seguridad y parches críticos por 5 a 8 años adicionales. Para sistemas legacy de misión crítica, ciertas empresas especializadas extienden este ciclo de mantenimiento hasta por 20 años mediante esquemas de soporte continuo (Sustaining Support).

Línea Temporal de las Versiones Principales

La siguiente tabla resume las versiones más importantes de Java y sus características destacadas:

Table 3:Evolución de Java por versiones

VersiónAñoCaracterísticas Destacadas
JDK 1.01996Lanzamiento inicial. AWT (interfaz gráfica básica), Applets
JDK 1.11997Clases internas (inner classes), JDBC (conexión a bases de datos), JavaBeans
J2SE 1.21998Collections Framework (listas, mapas, conjuntos), Swing (GUI mejorada), compilador JIT
J2SE 1.42002Assertions, NIO (entrada/salida no bloqueante), logging integrado, expresiones regulares
J2SE 5.02004Genéricos, enums, autoboxing, for-each (for (x : lista)), varargs, anotaciones
JDK 62006Mejoras de rendimiento, Scripting API (ejecutar JavaScript desde Java)
JDK 72011Try-with-resources, operador diamante (<>), strings en switch, NIO.2
JDK 82014Lambdas, Streams API, Optional, nueva API de Date/Time, métodos default en interfaces
JDK 92017Sistema de módulos (Jigsaw), JShell (REPL interactivo), métodos privados en interfaces
JDK 112018Primer release LTS, var para variables locales, HTTP Client moderno, eliminación de Java EE
JDK 172021Records, Sealed Classes, Pattern Matching para instanceof
JDK 212023Virtual Threads, Sequenced Collections, Pattern Matching en switch
JDK 252025Importación de módulos, constructores flexibles, tipos primitivos en pattern matching, archivos fuente compactos, Compact Object Headers

Vean JDK Releases para más información y detalles de los otros lanzamientos y evolución detallada de la plataforma.

¿Qué significan estos términos?

Algunos de los conceptos mencionados en la tabla se estudiarán durante el curso; otros son más avanzados:

Impacto y Legado

Java no es solo un lenguaje de programación; es una de las plataformas de software más influyentes de la historia. Su impacto se mide no solo en líneas de código escritas, sino en la infraestructura crítica del mundo moderno que depende de él.

¿Dónde se usa Java hoy?

El Ecosistema de Herramientas

Alrededor de Java se desarrolló un ecosistema maduro de herramientas que amplificaron su productividad:

Entornos de Desarrollo (IDEs)

Un IDE (Integrated Development Environment) es un programa que integra editor de código, compilador, depurador y otras herramientas en una sola aplicación.

Sistemas de Build (Construcción)

Un sistema de build automatiza la compilación, pruebas y empaquetado del software. En proyectos grandes con decenas de bibliotecas externas, gestionar esto manualmente sería imposible.

Frameworks de Aplicación

Un framework es un conjunto de bibliotecas y convenciones que proporcionan una estructura base para desarrollar aplicaciones.

Lenguajes sobre la JVM

La JVM demostró ser una plataforma tan robusta y bien optimizada que otros lenguajes fueron diseñados para ejecutarse sobre ella, aprovechando:

Kotlin

Desarrollado por JetBrains (los creadores de IntelliJ IDEA), Kotlin es un lenguaje moderno diseñado para ser 100% interoperable con Java: podés llamar código Java desde Kotlin y viceversa.

Características principales:

Kotlin es el lenguaje oficial para desarrollo Android desde 2019.

Scala

Scala (Scalable Language) combina programación orientada a objetos con programación funcional avanzada. Es conocido por:

Scala es el lenguaje detrás de Apache Spark, el framework dominante para procesamiento de datos a gran escala.

Groovy

Groovy es un lenguaje dinámico (los tipos se verifican en ejecución, no en compilación) diseñado para ser familiar a programadores Java pero más conciso.

Usos principales:

Clojure

Clojure es un dialecto de Lisp (un lenguaje de los años 50 conocido por su sintaxis de paréntesis) modernizado para la JVM. Se caracteriza por:

Java en la Actualidad y el Futuro

Estadísticas de Adopción

Según el índice TIOBE (que mide la popularidad de lenguajes según búsquedas en internet) y encuestas de Stack Overflow (la comunidad de programadores más grande del mundo), Java se mantiene consistentemente entre los 3 lenguajes más utilizados, junto con Python y C.

Su presencia es especialmente fuerte en sectores donde la estabilidad y el rendimiento son críticos:

Proyectos de Innovación Activos

Oracle y la comunidad OpenJDK continúan evolucionando la plataforma mediante proyectos específicos con nombres de código. Estos proyectos desarrollan características que eventualmente se incorporan al lenguaje:

Project Loom (Completado en Java 21)

Virtual Threads (hilos virtuales) permiten crear millones de hilos concurrentes con un costo mínimo de memoria. Antes de Loom, cada hilo de Java mapeaba a un hilo del sistema operativo, que consume aproximadamente 1MB de memoria. Con Virtual Threads, la JVM gestiona los hilos internamente, usando solo unos pocos kilobytes por hilo.

Esto es revolucionario para servidores que manejan muchas conexiones simultáneas (como servidores web o de bases de datos).

Project Amber (En progreso)

Mejoras en la sintaxis del lenguaje para hacer el código más conciso y expresivo:

Project Panama (En progreso)

Mejorar la interoperabilidad con código nativo (C, C++, bibliotecas del sistema). Históricamente, llamar a código nativo desde Java requería JNI (Java Native Interface), una API compleja y propensa a errores.

Panama introduce:

Project Valhalla (En progreso)

Value Types y genéricos especializados para eliminar el overhead de “boxing”. Actualmente, cuando guardás un int en una colección genérica como List<Integer>, Java debe “envolver” (box) el primitivo en un objeto, consumiendo memoria adicional.

Valhalla permitirá crear tipos de valor que se comporten como primitivos pero puedan usarse con genéricos.

GraalVM: El Futuro de la Ejecución

GraalVM es una máquina virtual de nueva generación desarrollada por Oracle Labs (el laboratorio de investigación de Oracle) que representa el futuro de la ejecución de código en la JVM.

¿Qué problemas resuelve?

La JVM tradicional tiene algunas limitaciones:

  1. Tiempo de inicio lento: La JVM necesita cargar clases, verificar bytecode y “calentar” el compilador JIT antes de alcanzar máximo rendimiento. Esto puede tomar segundos o incluso minutos.

  2. Alto consumo de memoria inicial: Incluso una aplicación simple consume decenas de megabytes porque la JVM reserva memoria para el JIT, metadatos de clases, etc.

  3. Barrera entre lenguajes: Llamar código Python desde Java (o viceversa) requiere mecanismos complejos y tiene overhead significativo.

Las soluciones de GraalVM

Compilación Ahead-of-Time (AOT) con Native Image:

GraalVM puede compilar una aplicación Java completa a un ejecutable nativo (como un programa C). El resultado:

Esto es ideal para:

Soporte políglota:

GraalVM puede ejecutar múltiples lenguajes en la misma máquina virtual con interoperabilidad nativa:

Los lenguajes pueden llamarse entre sí directamente, compartiendo objetos sin serialización.

GraalVM permite ejecutar múltiples lenguajes en una única máquina virtual con interoperabilidad nativa, sin las barreras tradicionales entre lenguajes.

Figure 3:GraalVM permite ejecutar múltiples lenguajes en una única máquina virtual con interoperabilidad nativa, sin las barreras tradicionales entre lenguajes.

Compilador optimizador avanzado:

El compilador de GraalVM (escrito en Java, irónicamente) produce código más optimizado que el compilador JIT tradicional de HotSpot para muchas cargas de trabajo, especialmente las que usan mucha programación funcional o abstracciones complejas.


Referencias Bibliográficas

Para profundizar en la historia técnica y social de Java, se recomiendan las siguientes fuentes:


Ejercicios

Solution to Exercise 1

En C, el programador es responsable de reservar (malloc) y liberar (free) la memoria. Si se olvida de liberar, se producen fugas de memoria (leaks); si la libera dos veces o accede a un puntero inválido, el programa falla de forma impredecible.

En Java, existe el Garbage Collector (GC), un proceso automático que identifica qué objetos ya no están en uso y libera su memoria.

Impacto en la seguridad:

  1. Eliminación de punteros colgantes: No podés acceder a una dirección de memoria que ya fue liberada.

  2. Prevención de Buffer Overflows: Java verifica los límites de los arreglos en tiempo de ejecución, impidiendo que un atacante escriba fuera del espacio asignado.

Solution to Exercise 2

Una máquina de pila (stack machine) realiza operaciones utilizando una estructura de datos de pila (LIFO). Por ejemplo, para sumar 2 + 3, el bytecode hace: push 2, push 3, add. El resultado queda en el tope de la pila.

Un procesador físico (Intel/ARM) usa registros (pequeñas memorias internas muy rápidas): MOV EAX, 2, ADD EAX, 3.

Diferencia clave:

  • La arquitectura de pila es más fácil de implementar para una máquina virtual y genera un bytecode muy compacto e independiente del hardware.

  • La arquitectura de registros es más rápida pero depende directamente del diseño específico del procesador.

Solution to Exercise 3

Los Virtual Threads (hilos virtuales) resuelven el problema de la concurrencia masiva. Los threads tradicionales del sistema operativo son recursos costosos:

  • Cada thread consume ~1MB de memoria para su stack.

  • El cambio de contexto entre threads tiene overhead significativo.

  • Un servidor típico puede manejar miles, pero no millones de threads.

Los Virtual Threads son:

  • Livianos: Consumen kilobytes, no megabytes.

  • Gestionados por la JVM: No mapean 1:1 con threads del SO.

  • Escalables: Podés crear millones de ellos.

  • Compatibles: Usan las mismas APIs que los threads tradicionales (Thread, ExecutorService).

Esto permite escribir código secuencial y bloqueante (más simple) mientras se obtiene la escalabilidad del código asíncrono.

Solution to Exercise 4

Java:

  • Estabilidad y compatibilidad hacia atrás garantizadas.

  • Mayor cantidad de desarrolladores disponibles en el mercado.

  • Escenarios: Sistemas bancarios legacy, aplicaciones empresariales de largo plazo.

Kotlin:

  • Sintaxis más concisa, null-safety integrado.

  • Coroutines nativas para programación asíncrona.

  • Escenarios: Desarrollo Android (lenguaje oficial), nuevos proyectos backend con Spring.

Scala:

  • Sistema de tipos avanzado, programación funcional de primera clase.

  • Inferencia de tipos sofisticada, pattern matching poderoso.

  • Escenarios: Procesamiento de datos con Apache Spark, sistemas que requieren modelado de dominio complejo.

La elección depende del equipo existente, los requisitos del proyecto y el ecosistema de bibliotecas necesarias.

Solution to Exercise 5

WORA significa que un programa Java, una vez compilado, puede ejecutarse en cualquier sistema operativo (Windows, Linux, macOS) y cualquier tipo de procesador (Intel, ARM, etc.) sin necesidad de recompilarlo o modificarlo.

Por qué fue revolucionario en 1995:

  • En esa época, cada combinación de sistema operativo y procesador requería compilar el programa específicamente para ella.

  • Un programa para Windows/Intel no funcionaba en Mac/Motorola ni en Unix/SPARC.

  • Esto multiplicaba el trabajo de desarrollo y testing por cada plataforma soportada.

  • La web estaba naciendo y necesitaba código que funcionara en cualquier navegador, sin importar el sistema del usuario.

El componente técnico que lo hace posible: la JVM (Java Virtual Machine)

El código Java se compila a bytecode, un formato intermedio que no es específico de ningún hardware. Luego, la JVM de cada plataforma traduce ese bytecode a instrucciones del procesador local. Cada sistema operativo tiene su propia implementación de la JVM, pero todas entienden el mismo bytecode estándar.

Solution to Exercise 6

La afirmación es parcialmente incorrecta y representa una visión desactualizada de Java.

Es verdad que:

  • Java inicialmente interpreta el bytecode, lo que es más lento que ejecutar código máquina directamente.

  • C++ se compila directamente a código máquina del procesador, sin intermediarios.

Pero la realidad moderna es más compleja:

La JVM incluye un compilador JIT (Just-In-Time) que detecta las partes del código que se ejecutan frecuentemente (“puntos calientes” o hotspots) y las compila a código máquina nativo mientras el programa se ejecuta.

Esto significa que:

  1. La primera vez que se ejecuta un método, se interpreta (lento).

  2. Después de varias ejecuciones, el JIT lo compila a código nativo (rápido).

  3. El JIT puede hacer optimizaciones dinámicas que un compilador estático como el de C++ no puede hacer, porque tiene información sobre el comportamiento real del programa en ejecución.

En la práctica, para aplicaciones de larga duración (servidores, por ejemplo), Java puede ser tan rápido o incluso más rápido que C++ en ciertos escenarios, gracias a las optimizaciones del JIT.

Donde Java sí es más lento es en el tiempo de inicio (la JVM necesita cargarse) y en el consumo de memoria (la JVM consume recursos adicionales). GraalVM con Native Image resuelve estos problemas.