Eu tenho um aplicativo java ee bastante grande com um enorme caminho de classe fazendo muito processamento xml. Atualmente, estou tentando acelerar algumas de minhas funções e localizar caminhos de código lento por meio de criadores de perfil de amostragem.
Uma coisa que notei é que especialmente partes do nosso código nas quais temos chamadas TransformerFactory.newInstance(...)
são desesperadamente lentas. Eu rastreei isso para o FactoryFinder
método findServiceProvider
sempre criando uma nova ServiceLoader
instância. No ServiceLoader
javadoc , encontrei a seguinte nota sobre cache:
Os fornecedores são localizados e instanciados preguiçosamente, ou seja, sob demanda. Um carregador de serviço mantém um cache dos provedores que foram carregados até o momento. Cada chamada do método iterador retorna um iterador que primeiro gera todos os elementos do cache, em ordem de instanciação, e depois localiza e instiga preguiçosamente todos os provedores restantes, adicionando cada um deles ao cache. O cache pode ser limpo através do método recarregar.
Por enquanto, tudo bem. Isso faz parte do FactoryFinder#findServiceProvider
método 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) {
...
}
}
Toda chamada para findServiceProvider
chamadas ServiceLoader.load
. Isso cria um novo ServiceLoader a cada vez. Dessa forma, parece que não há nenhum uso do mecanismo de cache do ServiceLoaders. Toda chamada verifica o caminho de classe para o ServiceProvider solicitado.
O que eu já tentei:
- Eu sei que você pode definir uma propriedade do sistema
javax.xml.transform.TransformerFactory
para especificar uma implementação específica. Dessa forma, o FactoryFinder não usa o processo ServiceLoader e é super rápido. Infelizmente, essa é uma propriedade ampla da jvm e afeta outros processos java em execução na minha jvm. Por exemplo, meu aplicativo é enviado com o Saxon e devo usar.com.saxonica.config.EnterpriseTransformerFactory
Eu tenho outro aplicativo que não é fornecido com o Saxon. Assim que eu defino a propriedade do sistema, meu outro aplicativo falha ao iniciar, porque não existecom.saxonica.config.EnterpriseTransformerFactory
no caminho de classe. Portanto, isso não parece ser uma opção para mim. - Eu já refatorei todos os lugares onde a
TransformerFactory.newInstance
é chamado e coloco em cache o TransformerFactory. Mas existem vários lugares nas minhas dependências onde não posso refatorar o código.
Minhas perguntas são: Por que o FactoryFinder não reutiliza um ServiceLoader? Existe uma maneira de acelerar todo esse processo do ServiceLoader além de usar propriedades do sistema? Isso não pôde ser alterado no JDK para que um FactoryFinder reutilize uma instância do ServiceLoader? Além disso, isso não é específico para um único FactoryFinder. Esse comportamento é o mesmo para todas as classes do FactoryFinder no javax.xml
pacote que eu analisei até agora.
Estou usando o OpenJDK 8/11. Meus aplicativos são implantados em uma instância do Tomcat 9.
Editar: fornecendo mais detalhes
Aqui está a pilha de chamadas para uma única chamada XMLInputFactory.newInstance:
Onde está a maioria dos recursos ServiceLoaders$LazyIterator.hasNextService
. Este método chama o getResources
ClassLoader para ler o META-INF/services/javax.xml.stream.XMLInputFactory
arquivo. Só essa ligação leva cerca de 35ms de cada vez.
Existe uma maneira de instruir o Tomcat a armazenar em cache melhor esses arquivos para que sejam exibidos mais rapidamente?
-D
sinalizador no seu Tomcat
processo? Por exemplo: -Djavax.xml.transform.TransformerFactory=<factory class>.
ele não deve substituir as propriedades de outros aplicativos. Sua postagem está bem descrita e você provavelmente já tentou, mas gostaria de confirmar. Consulte Como definir propriedade do sistema javax.xml.transform.TransformerFactory , Como definir HeapMemory ou JVM Arguments no Tomcat