Ich habe eine ziemlich große Java EE-Anwendung mit einem riesigen Klassenpfad, der viel XML-Verarbeitung ausführt. Derzeit versuche ich, einige meiner Funktionen zu beschleunigen und langsame Codepfade über Sampling-Profiler zu finden.
Eine Sache, die mir aufgefallen ist, ist, dass besonders Teile unseres Codes, in denen wir Anrufe haben, TransformerFactory.newInstance(...)
sehr langsam sind. Ich habe dies auf die FactoryFinder
Methode zurückgeführt, bei der findServiceProvider
immer eine neue ServiceLoader
Instanz erstellt wurde. In ServiceLoader
Javadoc habe ich den folgenden Hinweis zum Caching gefunden:
Anbieter werden träge lokalisiert und instanziiert, dh auf Anfrage. Ein Service Loader verwaltet einen Cache der Anbieter, die bisher geladen wurden. Jeder Aufruf der Iteratormethode gibt einen Iterator zurück, der zuerst alle Elemente des Caches in Instanziierungsreihenfolge liefert und dann alle verbleibenden Anbieter träge lokalisiert und instanziiert, wobei jeder nacheinander dem Cache hinzugefügt wird. Der Cache kann über die Reload-Methode geleert werden.
So weit, ist es gut. Dies ist ein Teil der OpenJDKs- FactoryFinder#findServiceProvider
Methode:
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) {
...
}
}
Jeder Anruf zu findServiceProvider
Anrufen ServiceLoader.load
. Dadurch wird jedes Mal ein neuer ServiceLoader erstellt . Auf diese Weise wird der Caching-Mechanismus von ServiceLoaders anscheinend überhaupt nicht verwendet. Jeder Aufruf durchsucht den Klassenpfad nach dem angeforderten ServiceProvider.
Was ich schon versucht habe:
- Ich weiß, dass Sie eine Systemeigenschaft
javax.xml.transform.TransformerFactory
festlegen können, um eine bestimmte Implementierung anzugeben. Auf diese Weise verwendet FactoryFinder den ServiceLoader-Prozess nicht und ist superschnell. Leider ist dies eine JVM-weite Eigenschaft und wirkt sich auf andere Java-Prozesse aus, die in meinem JVM ausgeführt werden. Zum Beispiel wird meine Anwendung mit Saxon ausgeliefert und sollte verwendet werden.com.saxonica.config.EnterpriseTransformerFactory
Ich habe eine andere Anwendung, die nicht mit Saxon geliefert wird. Sobald ich die Systemeigenschaft festgelegt habe, kann meine andere Anwendung nicht gestartet werden, da sich keinecom.saxonica.config.EnterpriseTransformerFactory
in ihrem Klassenpfad befindet. Das scheint mir also keine Option zu sein. - Ich habe bereits jeden Ort, an dem a
TransformerFactory.newInstance
aufgerufen wird, überarbeitet und die TransformerFactory zwischengespeichert. Es gibt jedoch verschiedene Stellen in meinen Abhängigkeiten, an denen ich den Code nicht umgestalten kann.
Meine Fragen sind: Warum verwendet FactoryFinder einen ServiceLoader nicht wieder? Gibt es eine andere Möglichkeit, diesen gesamten ServiceLoader-Prozess zu beschleunigen, als Systemeigenschaften zu verwenden? Könnte dies nicht im JDK geändert werden, sodass ein FactoryFinder eine ServiceLoader-Instanz wiederverwendet? Auch dies ist nicht spezifisch für einen einzelnen FactoryFinder. Dieses Verhalten ist für alle FactoryFinder-Klassen in dem javax.xml
Paket, das ich bisher angesehen habe, gleich.
Ich benutze OpenJDK 8/11. Meine Anwendungen werden in einer Tomcat 9-Instanz bereitgestellt.
Bearbeiten: Weitere Details bereitstellen
Hier ist der Aufrufstapel für einen einzelnen XMLInputFactory.newInstance-Aufruf:
Wo die meisten Ressourcen verwendet werden, befindet sich in ServiceLoaders$LazyIterator.hasNextService
. Diese Methode ruft getResources
ClassLoader auf, um die META-INF/services/javax.xml.stream.XMLInputFactory
Datei zu lesen . Allein dieser Anruf dauert jedes Mal ungefähr 35 ms.
Gibt es eine Möglichkeit, Tomcat anzuweisen, diese Dateien besser zwischenzuspeichern, damit sie schneller bereitgestellt werden?
-D
Flags für Ihren Tomcat
Prozess festzulegen? Beispiel: -Djavax.xml.transform.TransformerFactory=<factory class>.
Eigenschaften für andere Apps sollten nicht überschrieben werden. Ihr Beitrag ist gut beschrieben und wahrscheinlich haben Sie es versucht, aber ich möchte bestätigen. Siehe So legen Sie die Systemeigenschaft Javax.xml.transform.TransformerFactory fest , So legen Sie HeapMemory- oder JVM-Argumente in Tomcat fest