У мене досить великий додаток java ee з величезним classpath, що робить багато XML-обробки. В даний час я намагаюся пришвидшити деякі свої функції та знайти повільні шляхи коду за допомогою пробовідбірників.
Одне, що я помітив, - це те, що особливо частини нашого коду, у яких ми маємо дзвінки TransformerFactory.newInstance(...)
, відчайдушно повільні. Я простежив це до FactoryFinder
методу, findServiceProvider
завжди створюючи новий ServiceLoader
екземпляр. У ServiceLoader
javadoc я знайшов таку примітку про кешування:
Постачальники розташовані та інстанціровані ліниво, тобто на вимогу. Сервісний навантажувач підтримує кеш постачальників, завантажених до цього часу. Кожне виклик методу ітератора повертає ітератор, який спочатку видає всі елементи кешу, в порядку інстанції, а потім ліниво знаходить і інстанціює всі інші постачальники, додаючи кожного з них у кеш по черзі. Кеш можна очистити за допомогою методу перезавантаження.
Все йде нормально. Це частина FactoryFinder#findServiceProvider
методу 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) {
...
}
}
Кожен дзвінок на findServiceProvider
дзвінки ServiceLoader.load
. Це створює новий ServiceLoader кожного разу. Таким чином, здається, що механізм кешування ServiceLoaders взагалі не використовується. Кожен виклик сканує класний шлях для запитуваного ServiceProvider.
Що я вже пробував:
- Я знаю, що ви можете встановити властивість системи, як,
javax.xml.transform.TransformerFactory
щоб вказати конкретну реалізацію. Таким чином FactoryFinder не використовує процес ServiceLoader та його надшвидкий. На жаль, це властивість у форматі jvm і впливає на інші процеси Java, що працюють у моєму jvm. Наприклад, моя програма постачається з Saxon і повинна використовувати.com.saxonica.config.EnterpriseTransformerFactory
У мене є ще одна програма, яка не постачається з Saxon. Як тільки я встановив властивість системи, інша моя програма не запускається, оскільки її немаєcom.saxonica.config.EnterpriseTransformerFactory
на її класі. Тож це не здається для мене варіантом. - Я вже реконструював кожне місце, де
TransformerFactory.newInstance
викликається a , і кешую TransformerFactory. Але в моїх залежностях є різні місця, де я не можу переробити код.
Мої запитання: Чому FactoryFinder не повторно використовує ServiceLoader? Чи існує спосіб пришвидшити весь процес ServiceLoader, окрім використання системних властивостей? Чи не можна це змінити в JDK, щоб FactoryFinder повторно використовував екземпляр ServiceLoader? Також це не характерно для одного FactoryFinder. Ця поведінка однакова для всіх класів FactoryFinder в javax.xml
пакеті, який я розглянув досі.
Я використовую OpenJDK 8/11. Мої програми розміщені в екземплярі Tomcat 9.
Редагувати: надавати більше деталей
Ось стек викликів для одного виклику XMLInputFactory.newInstance:
Там, де використовується більшість ресурсів, знаходиться в ServiceLoaders$LazyIterator.hasNextService
. Цей метод закликає getResources
ClassLoader прочитати META-INF/services/javax.xml.stream.XMLInputFactory
файл. Один лише цей дзвінок займає приблизно 35 мс кожного разу.
Чи є спосіб доручити Tomcat краще кешувати ці файли, щоб вони швидше подавались?
-D
прапорця для вашого Tomcat
процесу? Наприклад: -Djavax.xml.transform.TransformerFactory=<factory class>.
він не повинен змінювати властивості для інших додатків. Ваше повідомлення добре описано, і, ймовірно, ви його спробували, але я хотів би підтвердити. Див. Як встановити властивість системи Javax.xml.transform.TransformerFactory , як встановити аргументи HeapMemory або JVM в Tomcat