Ho un'applicazione Java ee piuttosto grande con un enorme percorso di classe che fa molta elaborazione XML. Attualmente sto cercando di accelerare alcune delle mie funzioni e di individuare percorsi di codice lenti tramite i profiler di campionamento.
Una cosa che ho notato è che specialmente parti del nostro codice in cui abbiamo chiamate simili TransformerFactory.newInstance(...)
sono disperatamente lente. Ho rintracciato questo FactoryFinder
metodo per findServiceProvider
creare sempre una nuova ServiceLoader
istanza. In ServiceLoader
javadoc ho trovato la seguente nota sulla memorizzazione nella cache:
I provider sono localizzati e istanziati pigramente, ovvero su richiesta. Un caricatore di servizi mantiene una cache dei provider che sono stati caricati finora. Ogni invocazione del metodo iteratore restituisce un iteratore che dapprima produce tutti gli elementi della cache, in ordine di istanza, quindi individua pigramente e crea un'istanza di tutti i provider rimanenti, aggiungendoli a turno alla cache. La cache può essere cancellata tramite il metodo di ricarica.
Fin qui tutto bene. Questa è una parte del FactoryFinder#findServiceProvider
metodo 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) {
...
}
}
Ogni chiamata alle findServiceProvider
chiamate ServiceLoader.load
. Questo crea un nuovo ServiceLoader ogni volta. In questo modo sembra che non ci sia alcun uso del meccanismo di memorizzazione nella cache di ServiceLoaders. Ogni chiamata esegue la scansione del percorso di classe per il ServiceProvider richiesto.
Quello che ho già provato:
- So che puoi impostare una proprietà di sistema come
javax.xml.transform.TransformerFactory
specificare un'implementazione specifica. In questo modo FactoryFinder non utilizza il processo ServiceLoader ed è super veloce. Purtroppo questa è una proprietà jvm wide e influenza altri processi java in esecuzione nel mio jvm. Ad esempio, la mia applicazione viene fornita con Saxon e dovrei usarecom.saxonica.config.EnterpriseTransformerFactory
Ho un'altra applicazione che non viene fornita con Saxon. Non appena imposto la proprietà di sistema, la mia altra applicazione non si avvia, perché non c'ècom.saxonica.config.EnterpriseTransformerFactory
sul suo percorso di classe. Quindi questa non sembra essere un'opzione per me. - Ho già eseguito il refactoring di tutti i luoghi in cui
TransformerFactory.newInstance
viene chiamato a e memorizzo nella cache TransformerFactory. Ma ci sono vari posti nelle mie dipendenze in cui non posso refactificare il codice.
Le mie domande sono: Perché FactoryFinder non riutilizza un ServiceLoader? C'è un modo per accelerare l'intero processo di ServiceLoader oltre all'utilizzo delle proprietà di sistema? Non è possibile modificarlo in JDK in modo che un FactoryFinder riutilizzi un'istanza di ServiceLoader? Inoltre, questo non è specifico per un singolo FactoryFinder. Questo comportamento è lo stesso per tutte le classi FactoryFinder nel javax.xml
pacchetto che ho esaminato finora.
Sto usando OpenJDK 8/11. Le mie applicazioni sono distribuite in un'istanza di Tomcat 9.
Modifica: fornisce maggiori dettagli
Ecco lo stack di chiamate per una singola chiamata XMLInputFactory.newInstance:
Dove si trova la maggior parte delle risorse ServiceLoaders$LazyIterator.hasNextService
. Questo metodo chiama getResources
ClassLoader per leggere il META-INF/services/javax.xml.stream.XMLInputFactory
file. Quella chiamata da sola richiede circa 35 ms ogni volta.
C'è un modo per istruire Tomcat a memorizzare meglio questi file in modo che vengano pubblicati più velocemente?
-D
flag per il tuo Tomcat
processo? Ad esempio: -Djavax.xml.transform.TransformerFactory=<factory class>.
non deve sovrascrivere le proprietà di altre app. Il tuo post è ben descritto e probabilmente l'hai provato ma vorrei confermare. Vedi Come impostare la proprietà di sistema Javax.xml.transform.TransformerFactory , Come impostare gli argomenti HeapMemory o JVM in Tomcat