Contenido


Introducción a la ocupación múltiple de Java

Aprenda sobre una nueva función para los sistemas de nube en la versión IBM Java 7 R1

Comments

Los proveedores de nube deberán ponderar el costo de la infraestructura que se requiere para operar sistemas y entregar servicios contra los beneficios que derivan los proveedores. Estas consideraciones de costo y beneficio están empujando a los proveedores a considerar varias arquitecturas. Sus elecciones oscilan entre un espectro de arquitecturas entre no compartir a ocupación múltiple compartida . Con la arquitectura desde no compartir, el proveedor ofrece hardware, software y aplicaciones plenamente dedicadas a los clientes individuales. En la ocupación múltiple compartida, las aplicaciones de clientes múltiples se soportan usando una sola aplicación y se comparte todo el hardware y software implícito.

La principal compensación al continuar a través de este espectro arquitectónico es el aislamiento versus densidad. La densidad es el número de sistemas y servicios que puede darles para un conjunto específico de hardware y software. Mientras más recursos se compartan, mayor la densidad. La mayor densidad baja los costos del proveedor. Al mismo tiempo, el compartir más reduce el nivel de aislamiento entre ocupantes — los sistemas o servicios individuales que se están entregando. El aislamiento es el grado al que un tenedor puede afectar la actividad y los datos de otros ocupantes.

Para los ocupantes con base en Java, las posiciones a lo largo del espectro arquitectónico incluyen ya sea el compartir o no compartir el JVM. En cualquier arquitectura en la que se comparta la aplicación de alto nivel deberá compartirse el JVM. El compartir el JVM ahorra tanto el tiempo de la memoria como del procesador. Pero con la tecnología de JVM tradicional, el compartir el JVM normalmente remueve cualquier aislamiento de la capa de la infraestructura, requiriendo que la propia aplicación de alto nivel proporcione aislamiento. Este artículo introduce una función de ocupante múltiple que está disponible para el uso de prueba en la versión7 R1 de IBM como vista previa técnica (consulte Temas relacionados). Esta función permite el despliegue para obtener las ventajas de compartir JVM al mismo tiempo que se mantiene el aislamiento que puede lograrse cuando se comparte un JVM tradicional.

Beneficios y costos de un JVM de ocupantes múltiples

El principal beneficio de usar un JVM de ocupantes múltiples es que los despliegues evitan el consumo de memoria que se asocia típicamente con el uso de JVMs de estándar múltiple. Este consumo tiene varias causas:

  • El heap de Java consume cientos de megabytes de memoria. Los objetos de heap no pueden compartirse con los JVMs, aun cuando los objetos sean idénticos. Además, los JVMs tienden a usar todo el heap que se les asigna aun cuando necesiten la cantidad máxima por poco tiempo.
  • El compilador justo a tiempo (JIT) consume dieces de megabytes de memoria, porque el código generado es privado y consume memoria. El código generado también toma significativos ciclos del procesador para producirse, lo cual le roba tiempo de las aplicaciones.
  • Los artefactos internos para la clases (muchas de las cuales, tales como String y Hashtable, existen para todas las aplicaciones) consumen memoria. Existe una instancia de cada uno de estos artefactos para cada JVM.
  • Cada JVM tiene un asistente colector de basura de hilos por núcleo de manera predeterminada y también tiene hilos múltiples de compilación. La actividad de compilación o de colección de basura puede ocurrir simultáneamente en uno o más de los JVMs, el cual puede ser menor a lo óptimo dado que los JVMs competirán por un tiempo de procesador limitado.

Además de bajar los costos de la memoria y el procesamiento , el JVM de ocupante múltiple también le provee un mayor aislamiento que el operar aplicaciones múltiples en un sólo JVM tradicional.

Otro beneficio es que después de que se inicie el primer ocupante del JVM, las aplicaciones subsecuentes requieren menos tiempo para empezar porque el JVM ya está corriendo. Los tiempos de inicio reducidos son particularmente útiles para las aplicaciones de corta operación que a veces se usan para la creación de guiones.

El costo principal de usar JVM’s de ocupante múltiple es que los ocupantes están menos aislados que las aplicaciones múltiples que operan en los JVM’s separados. Por ejemplo, una falla nativa en el JVM de ocupante múltiple afecta a todos los ocupantes.

También, resulta en una pequeña carga en el desempeño debido al trabajo que deberá realizar el JVM para implementar las extensiones de ocupación múltiple. Sin embargo, el impacto en este golpe de desempeño reduce el número de incrementos de los ocupantes — porque evita el costo de correr JVMs múltiples del procesador y memoria en el mismo sistema.

Usando el JVM de ocupante múltiple

Para optar por compartir el tiempo de ejecución con otros ocupantes, el usuario de la aplicación usa un sólo argumento, -Xmt, a la línea de comando cuando se lanza la aplicación. Por ejemplo:

java -Xmt -jar one.jar

El resultado es que la aplicación se comporta (en sujeción a las limitaciones que describimos posteriormente en este artículo) como si estuviera corriendo en un JVM dedicado. Pero en realidad opera lado a lado con otras aplicaciones. Las extensiones en el JVM de ocupante múltiple hacen posible el lanzamiento de este modo y proveen el aislamiento entre los ocupantes que están compartiendo el JVM.

Cuando se lanza un ocupante, el lanzador de JVM ya sea localiza el daemon de JVM compartido existente (javad) o lo inicia si es necesario, como se ilustra en la Figura 1:

Figura 1. El lanzador JVM localiza (y si es necesario inicia) el daemon de JVM compartido automáticamente
Screen capture and diagram represents the JVM launcher locating and starting the shared JVM daemon (javad)
Screen capture and diagram represents the JVM launcher locating and starting the shared JVM daemon (javad)

Cuando se inicia un segundo ocupante, ese ocupante encuentra el daemon de JVM compartido y lo opera dentro de ese JVM, como se ilustra en la Figura 2:

Figura 2. El lanzador de JVM se ubica y conecta con el daemon de JVM existente
Screen capture and diagram represents the JVM launcher locating and connecting to the existing JVM daemon (javad)
Screen capture and diagram represents the JVM launcher locating and connecting to the existing JVM daemon (javad)

El resultado es que una copia del código de bootstrap común a ambos ocupantes vive en el proceso javad. Este arreglo hace posible que los ocupantes compartan gran parte de las estructuras del tiempo de ejecución.

Es fácil correr las aplicaciones existentes usando el JVM de ocupantes múltiples porque sólo se requieren cambios limitados a la línea de comando.

Logrando el aislamiento

Dos o más aplicaciones que corren en el mismo JVM (convencional) normalmente no se aislarían unos de los otros. La actividad de cada aplicación afectará a la que la otra puede hacer. Además, los datos que pueden compartirse a través de los cambios estáticos estarían accesibles a todas las aplicaciones. El JVM de ocupantes múltiples resuelve estos temas de dos maneras: aislamiento de campo estático y restricciones de recursos .

Aislamiento de campo estático

En el JVM de ocupante múltiple, se comparten las partes no variantes entre los ocupantes. Estas partes incluyen los códigos compilados para los métodos, las estructuras de datos que usa JVM y otros artefactos similares. Esta forma de compartir resulta en ahorros de memoria dado que las copias separadas que existirían si se usaran JVMs múltiples son innecesarias. Sin embargo, el JVM de ocupante múltiple le otorga a cada ocupante su propia copia de campos estáticos. En virtud del aislamiento del campo estático — junto con el hecho de que cada ocupante puede generalmente sólo tener acceso a las instancias de los objetos que creó — cada uno de los ocupantes sólo puede acceder a los datos asociados consigo mismo. El resultado es el aislamiento de datos entre ocupantes.

Restricciones de recursos: Lidiar con la mala conducta

En un mundo perfecto, los ocupantes operarán conjuntamente y usarán recursos compartidos de manera apropiada. Sin embargo, los errores y la conducta maliciosa pueden ocurrir ambos en este mundo imperfecto. El JVM de ocupante múltiple provee controles que pueden configurarse para limitar la habilidad del ocupante para portarse mal y usar recursos de manera que afecten a los demás ocupantes. Los valores que pueden controlarse incluyen:

  • Tiempo del procesador
  • Tamaño de Heap
  • Conteo de hilo
  • Archivo I/O: ancho de banda de lectura, ancho de bando de escritura
  • Enchufe de entrada/salida: ancho de banda de lectura, ancho de banda de escritura

Estos controles se especifican en la línea de comando -Xmt. Por ejemplo:

  • -Xlimit:cpu=10-30 (CPU mínimo de 10 por ciento, 30 por ciento máximo)
  • -Xlimit:cpu=30 (CPU 30 por ciento máximo)
  • -Xlimit:netIO=20M (ancho de banda máximo de 20 Mbps)
  • -Xms8m-Xmx64m (heap inicial de 8 MB, 64 MB máximo)

La documentación de Java 7 R1 incluye información sobre todas las opciones disponibles (consulte Temas relacionados).

Desempeño y huella

Como prueba para comparar el desempeño de la aplicación y huella de memoria en los JVMs no compartidos y de ocupante múltiples, agregamos aplicaciones a cada configuración de JVM hasta que se intercambia el sistema. (Cuando se intercambia consideramos que el sistema está “lleno”). En un caso no compartido corremos la aplicación en un JVM separado e iniciamos un nuevo JVM para cada aplicación adicional. En el caso del ocupante múltiple, corremos la aplicación como otro ocupante en el JVM de ocupante múltiple sencillo.

Tabla 1 y Tabla 2 muestra los resultados que hemos logrado usando una máquina con 1 GB de memoria y un JVM de 64-bits (el JVM de referencias comprimido con la política de recolección de basura equilibrada en todo los casos). La columna "sintonizada manualmente” en ambas tablas muestra los resultados de los JVM regulares después de haber sintonizado manualmente las opciones de la línea de comando para tratar de lograr la mejor densidad posible (Tabla 1) o los tiempos de arranque (Table 2). La columna predeterminada muestra los resultados para el JVM regular con las opciones predeterminadas.

El JVM de ocupante múltiple logró 1.2x a 4.9x veces la densidad del JVM no compartido — dependiendo de la aplicación — como se muestra en la Tabla 1:

Tabla 1. Número máximo de aplicaciones concurrentes
AplicaciónDescripciónOcupante múltipleSintonizado a mano PredeterminadoMejoría con JVM de ocupante múltiple
Hola Mundo Imprima "Hola Mundo" y después duerma30973634.2X a 4.9X
JettyInicie Jetty y espere las solicitudes 34-181.9X
TomcatInicie Tomcat y espere las solicitudes 28-132.1X
JRubyEmpiece JRuby y espere las solicitudes 3226151.2X a 2.1X

Los resultados de la creciente densidad de compartir los artefactos claves, incluyendo:

  • Clases y artefactos relacionaos que se cargan por el bootstrap y los cargadores de clase de las extensiones, el heap Clase objeto para cada una de las clases que cargan los cargadores y objetos heap que pueden compartirse de manera segura a través de los ocupantes (por ejemplo, Strings internados).
  • código compilado por JIT y metadatos para las clases compiladas para JIT.
  • Heap: Un ocupante puede usar espacio que está disponible en el heap cuando no se requiere por otros ocupantes.

La Tabla 2 muestra que logramos 1.2x a 6x más rápido que los tiempos de arranque promedio con el JVM del ocupante múltiple:

Tabla 2. Tiempo de arranque (primero/promedio)
AplicaciónDescripciónOcupante múltiple Sintonizado a mano PredeterminadoMejora con el JVM del propietario múltiple
Hola Mundo WorldImprima "Hola Mundo" y después duerma 5709/138ms514/400ms3361/460ms3.3X
JettyEmpiece Jetty y espere solicitudes 7478/2116ms-6296/12624ms6X
TomcatInicie Tomcat y espere las solicitudes 9333/6005ms-7802/7432ms1.2X
JRubyInicie JRuby y espere las solicitudes 12391/3277ms14847/4101ms7849/6058ms1.25X a 1.8X

In Tabla 2, se puede ver que el tiempo de arranque para la instancia de la primera aplicación es en general más lenta en el JVM de ocupante múltiple que en el JVM estándar. Se espera este resultado porque la primera instancia incurre en cierto retraso adicional del arranque en virtud de la longitud adicional de la ruta causada por extensiones de propietario múltiple. El tiempo de arranque de las instancias subsecuentes es consistentemente mejor para el JVM de propietario múltiple.

Estos primeros resultados se produjeron con el los JVMs de desarrollo y se pueden hacer más mejoras. Además, estos ejemplos no se factorizan en la forma para compartir que puede realizarse cuando las aplicaciones necesitan recursos en diferentes tiempos. En un JVM típico, la huella de memoria para cada JVM tiende a crecer hacia el techo que requiere a través de su ciclo de vida. En el JVM estándar gran parte del JVM de esta huella no se comparte. Con el JVM del propietario múltiple, si no se traslapan los requerimientos, la memoria para heap y los artefactos nativos pueden compartirse más fácilmente.

Limitaciones

Un objetivo del JVM de ocupante múltiple es ser capaz de correr todas las aplicaciones de Java sin modificación. Esto no es posible actualmente en virtud de las limitaciones que imponen las especificaciones de Java y las limitaciones en nuestra implementación actual. Las limitaciones conocidas claves incluyen:

  • Java Native Interface (JNI) nativas: El JVM de ocupante múltiple no provee aislamiento para JNI nativos. Las aplicaciones con JNI nativos suministrados por el usuario pueden no ser seguros de operarse con el JVM de ocupante múltiple. Dichas aplicaciones pueden afectar la operación del JVM general y acceder a datos de otros ocupantes. En los casos que conlleven suficiente “confianza” en los nativos (por ejemplo, middleware bien conocido), el riesgo puede ser aceptable. Además del OS permite que el proceso JVM compartido solo cargue una copia de la biblioteca compartida, que es donde se localizan los nativos. El resultado es que los ocupantes múltiples no cargan los mismos nativos están en la misma biblioteca compartida.
  • Java Virtual Machine Tool Interface (JVMTI): Dado que las actividades de depuración y de creación de perfiles afectan a todos los ocupantes que comparten el servidor JVM, estas funciones ya no se soportan en el JDK de ocupante múltiple. Esta es una área en la que planeamos más trabajo.
  • Programas GUI: Aparecen las bibliotecas tales como SWT para mantener el estado global en la capa nativa y por lo tanto no están soportadas en el JDK del ocupante múltiple.

Conclusión

Este artículo introdujo el JVM del ocupante múltiple, la forma en la que se usa y los costos y beneficios de usarlo. Esperamos que hayamos sido capaces de crear su interés y de que pruebe beta y nos de su retroalimentación. Creemos que el JVM de ocupante múltiple puede proporcionar beneficios significativos para los ambientes correctos.


Recursos para Descargar


Temas relacionados


Comentarios

Inicie Sesión o Regístrese para agregar comentarios.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java, Cloud computing
ArticleID=981007
ArticleTitle=Introducción a la ocupación múltiple de Java
publish-date=05122014