我有一个相当大的 java ee 应用程序,它有一个巨大的类路径,可以进行大量的 xml 处理。目前,我正在尝试加快一些功能的速度,并通过采样分析器找到缓慢的代码路径。
我注意到的一件事是,特别是我们的代码中包含类似调用的部分TransformerFactory.newInstance(...)
速度非常慢。我追踪到这个FactoryFinder
方法findServiceProvider
总是创建一个新ServiceLoader
实例。在ServiceLoader
javadoc中我发现了以下关于缓存的注释:
提供者是延迟定位和实例化的,即按需定位和实例化。服务加载器维护迄今为止已加载的提供者的缓存。每次调用迭代器方法都会返回一个迭代器,该迭代器首先按实例化顺序生成缓存的所有元素,然后延迟查找并实例化任何剩余的提供程序,依次将每个元素添加到缓存中。可以通过reload方法清除缓存。
到目前为止,一切都很好。这是 OpenJDK 方法的一部分FactoryFinder#findServiceProvider
:
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 范围的属性,会影响我的 jvm 中运行的其他 java 进程。例如,我的应用程序随 Saxon 一起提供,应该使用com.saxonica.config.EnterpriseTransformerFactory
我有另一个不随 Saxon 提供的应用程序。一旦我设置了系统属性,我的另一个应用程序就无法启动,因为com.saxonica.config.EnterpriseTransformerFactory
它的类路径上没有。所以这对我来说似乎不是一个选择。
我已经重构了调用 a 的每个地方TransformerFactory.newInstance
并缓存了 TransformerFactory。但我的依赖项中有很多地方无法重构代码。
我的问题是:为什么 FactoryFinder 不重用 ServiceLoader?除了使用系统属性之外,还有其他方法可以加快整个 ServiceLoader 进程吗?难道不能在 JDK 中对此进行更改,以便 FactoryFinder 重用 ServiceLoader 实例吗?此外,这并不特定于单个 FactoryFinder。javax.xml
对于我迄今为止查看过的包中的所有 FactoryFinder 类,此行为都是相同的。
我正在使用 OpenJDK 8/11。我的应用程序部署在 Tomcat 9 实例中。
Smart猫小萌
慕姐4208626
相关分类