Tengo una aplicación Java ee bastante grande con un gran classpath que hace mucho procesamiento xml. Actualmente estoy tratando de acelerar algunas de mis funciones y localizar rutas de código lentas a través de perfiladores de muestreo.
Una cosa que noté es que especialmente las partes de nuestro código en las que tenemos llamadas TransformerFactory.newInstance(...)
son extremadamente lentas. Seguí esto hasta el FactoryFinder
método, findServiceProvider
siempre creando una nueva ServiceLoader
instancia. En ServiceLoader
javadoc encontré la siguiente nota sobre el almacenamiento en caché:
Los proveedores se ubican y crean instancias de manera perezosa, es decir, bajo demanda. Un cargador de servicios mantiene un caché de los proveedores que se han cargado hasta ahora. Cada invocación del método iterador devuelve un iterador que primero produce todos los elementos de la memoria caché, en orden de creación de instancias, y luego localiza e instancia perezosamente a los proveedores restantes, agregando cada uno a la memoria caché a su vez. El caché se puede borrar mediante el método de recarga.
Hasta aquí todo bien. Esto es parte del FactoryFinder#findServiceProvider
método OpenJDKs :
private static <T> T findServiceProvider(final Class<T> type)
throws TransformerFactoryConfigurationError
{
try {
return AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
final Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
return null;
}
}
});
} catch(ServiceConfigurationError e) {
...
}
}
Cada llamada a las findServiceProvider
llamadas ServiceLoader.load
. Esto crea un nuevo ServiceLoader cada vez. De esta manera, parece que no hay ningún uso del mecanismo de almacenamiento en caché de ServiceLoaders. Cada llamada escanea el classpath para el ServiceProvider solicitado.
Lo que ya he probado:
- Sé que puede establecer una propiedad del sistema
javax.xml.transform.TransformerFactory
para especificar una implementación específica. De esta manera FactoryFinder no utiliza el proceso ServiceLoader y es súper rápido. Lamentablemente, esta es una propiedad amplia de jvm y afecta a otros procesos de Java que se ejecutan en mi jvm. Por ejemplo, mi aplicación se envía con Saxon y debería usarcom.saxonica.config.EnterpriseTransformerFactory
Tengo otra aplicación que no se envía con Saxon. Tan pronto como configuro la propiedad del sistema, mi otra aplicación no se inicia porque no hay ningunacom.saxonica.config.EnterpriseTransformerFactory
en su classpath. Así que esto no parece ser una opción para mí. - Ya refactoricé todos los lugares donde
TransformerFactory.newInstance
se llama a y almacené en caché TransformerFactory. Pero hay varios lugares en mis dependencias donde no puedo refactorizar el código.
Mi pregunta es: ¿Por qué FactoryFinder no reutiliza un ServiceLoader? ¿Hay alguna manera de acelerar todo este proceso de ServiceLoader que no sea usar las propiedades del sistema? ¿No podría cambiarse esto en el JDK para que FactoryFinder reutilice una instancia de ServiceLoader? Además, esto no es específico de un único FactoryFinder. Este comportamiento es el mismo para todas las clases de FactoryFinder en el javax.xml
paquete que he visto hasta ahora.
Estoy usando OpenJDK 8/11. Mis aplicaciones se implementan en una instancia de Tomcat 9.
Editar: proporcionar más detalles
Aquí está la pila de llamadas para una sola llamada XMLInputFactory.newInstance:
Donde se utilizan la mayoría de los recursos es en ServiceLoaders$LazyIterator.hasNextService
. Este método llama getResources
a ClassLoader para leer el META-INF/services/javax.xml.stream.XMLInputFactory
archivo. Esa llamada solo toma alrededor de 35 ms cada vez.
¿Hay alguna manera de indicarle a Tomcat que guarde mejor estos archivos en caché para que se sirvan más rápido?
-D
flag en tu Tomcat
proceso? Por ejemplo: -Djavax.xml.transform.TransformerFactory=<factory class>.
no debe anular las propiedades de otras aplicaciones. Su publicación está bien descrita y probablemente lo haya intentado, pero me gustaría confirmarlo. Consulte Cómo configurar la propiedad del sistema Javax.xml.transform.TransformerFactory , Cómo configurar HeapMemory o JVM Arguments en Tomcat