У меня довольно большое приложение Java ee с огромным classpath, выполняющим большую обработку xml. В настоящее время я пытаюсь ускорить некоторые из моих функций и найти медленные пути кода с помощью выборочных профилировщиков.
Одна вещь, которую я заметил, заключается в том, что особенно части нашего кода, в которых мы имеем такие вызовы TransformerFactory.newInstance(...)
, крайне медленны. Я проследил это до FactoryFinder
метода, findServiceProvider
всегда создающего новый ServiceLoader
экземпляр. В ServiceLoader
Javadoc я нашел следующее примечание о кешировании:
Поставщики расположены и создаются лениво, то есть по требованию. Загрузчик службы поддерживает кэш поставщиков, которые были загружены до сих пор. Каждый вызов метода итератора возвращает итератор, который сначала возвращает все элементы кэша в порядке создания экземпляров, а затем лениво находит и создает экземпляры всех оставшихся поставщиков, добавляя каждого из них в кэш по очереди. Кэш может быть очищен с помощью метода перезагрузки.
Все идет нормально. Это часть FactoryFinder#findServiceProvider
метода 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) {
...
}
}
Каждый звонок на findServiceProvider
звонки ServiceLoader.load
. Это создает новый ServiceLoader каждый раз. Таким образом, кажется, что механизм кэширования ServiceLoaders вообще не используется. Каждый вызов сканирует путь к классу для запрашиваемого ServiceProvider.
Что я уже пробовал:
- Я знаю, что вы можете установить системное свойство, например,
javax.xml.transform.TransformerFactory
чтобы указать конкретную реализацию. Таким образом, FactoryFinder не использует процесс ServiceLoader и его супер быстрый. К сожалению, это свойство jvm wide и влияет на другие процессы java, работающие в моем jvm. Например, мое приложение поставляется с Saxon и должно использовать.com.saxonica.config.EnterpriseTransformerFactory
У меня есть другое приложение, которое не поставляется с Saxon. Как только я установлю системное свойство, мое другое приложение не сможет запуститься, потому чтоcom.saxonica.config.EnterpriseTransformerFactory
его путь к классу отсутствует. Так что это, кажется, не вариант для меня. - Я уже провел рефакторинг каждого места, где
TransformerFactory.newInstance
вызывается и кеширует 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