J'ai une application Java ee assez grande avec un énorme chemin de classe faisant beaucoup de traitement xml. Actuellement, j'essaie d'accélérer certaines de mes fonctions et de localiser des chemins de code lents via des profils d'échantillonnage.
Une chose que j'ai remarquée, c'est que les parties de notre code dans lesquelles nous avons des appels comme TransformerFactory.newInstance(...)
sont désespérément lentes. J'ai suivi cela jusqu'à la FactoryFinder
méthode findServiceProvider
créant toujours une nouvelle ServiceLoader
instance. En ServiceLoader
javadoc, j'ai trouvé la note suivante sur la mise en cache:
Les fournisseurs sont localisés et instanciés paresseusement, c'est-à-dire à la demande. Un chargeur de service conserve un cache des fournisseurs qui ont été chargés jusqu'à présent. Chaque invocation de la méthode itérateur renvoie un itérateur qui produit d'abord tous les éléments du cache, dans l'ordre d'instanciation, puis localise et instancie paresseusement tous les fournisseurs restants, en les ajoutant chacun au cache à son tour. Le cache peut être effacé via la méthode de rechargement.
Jusqu'ici tout va bien. Cela fait partie de la FactoryFinder#findServiceProvider
méthode OpenJDK :
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) {
...
}
}
Chaque appel aux findServiceProvider
appels ServiceLoader.load
. Cela crée un nouveau ServiceLoader à chaque fois. De cette façon, il semble qu'il n'y ait aucune utilisation du mécanisme de mise en cache de ServiceLoaders. Chaque appel analyse le chemin de classe pour le ServiceProvider demandé.
Ce que j'ai déjà essayé:
- Je sais que vous pouvez définir une propriété système comme
javax.xml.transform.TransformerFactory
pour spécifier une implémentation spécifique. De cette façon, FactoryFinder n'utilise pas le processus ServiceLoader et son super rapide. Malheureusement, c'est une propriété jvm large et affecte d'autres processus java exécutés dans mon jvm. Par exemple, mon application est livrée avec Saxon et devrait utilisercom.saxonica.config.EnterpriseTransformerFactory
J'ai une autre application qui n'est pas livrée avec Saxon. Dès que j'ai défini la propriété système, mon autre application ne démarre pas, car il n'y en a pascom.saxonica.config.EnterpriseTransformerFactory
sur son chemin de classe. Donc, cela ne semble pas être une option pour moi. - J'ai déjà refactorisé chaque endroit où un
TransformerFactory.newInstance
est appelé et mis en cache TransformerFactory. Mais il y a plusieurs endroits dans mes dépendances où je ne peux pas refactoriser le code.
Ma question est la suivante: pourquoi FactoryFinder ne réutilise-t-il pas un ServiceLoader? Existe-t-il un moyen d'accélérer l'ensemble de ce processus ServiceLoader autre que l'utilisation des propriétés système? Cela ne pourrait-il pas être modifié dans le JDK afin qu'un FactoryFinder réutilise une instance de ServiceLoader? De plus, cela n'est pas spécifique à un seul FactoryFinder. Ce comportement est le même pour toutes les classes FactoryFinder dans le javax.xml
package que j'ai examiné jusqu'à présent.
J'utilise OpenJDK 8/11. Mes applications sont déployées dans une instance de Tomcat 9.
Modifier: fournir plus de détails
Voici la pile d'appels pour un seul appel XMLInputFactory.newInstance:
Où la plupart des ressources sont utilisées se trouve ServiceLoaders$LazyIterator.hasNextService
. Cette méthode appelle getResources
ClassLoader pour lire le META-INF/services/javax.xml.stream.XMLInputFactory
fichier. Cet appel à lui seul prend environ 35 ms à chaque fois.
Existe-t-il un moyen de demander à Tomcat de mieux mettre ces fichiers en cache afin qu'ils soient servis plus rapidement?
-D
indicateur sur votre Tomcat
processus? Par exemple: -Djavax.xml.transform.TransformerFactory=<factory class>.
il ne doit pas remplacer les propriétés d'autres applications. Votre message est bien décrit et vous l'avez probablement essayé, mais je voudrais le confirmer. Voir Comment définir la propriété système Javax.xml.transform.TransformerFactory , Comment définir les arguments HeapMemory ou JVM dans Tomcat