java - FactoryFinder 性能/缓存不良

标签 java serviceloader

我有一个相当大的java ee应用程序,它有一个巨大的类路径,可以进行大量的xml处理。目前,我正在尝试加快一些功能的速度,并通过采样分析器找到缓慢的代码路径。

我注意到的一件事是,特别是我们的代码中,我们有像 TransformerFactory.newInstance(...) 这样的调用。非常慢。我追踪到FactoryFinder方法findServiceProvider总是创造一个新的ServiceLoader实例。在 ServiceLoader javadoc我发现以下有关缓存的注释:

Providers are located and instantiated lazily, that is, on demand. A service loader maintains a cache of the providers that have been loaded so far. Each invocation of the iterator method returns an iterator that first yields all of the elements of the cache, in instantiation order, and then lazily locates and instantiates any remaining providers, adding each one to the cache in turn. The cache can be cleared via the reload method.

到目前为止一切顺利。这是 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。

我已经尝试过:

  1. 我知道您可以设置一个系统属性,例如 javax.xml.transform.TransformerFactory来指定具体的实现。这样FactoryFinder就不会使用ServiceLoader进程而且速度 super 快。遗憾的是,这是一个 jvm 范围的属性,会影响我的 jvm 中运行的其他 java 进程。例如,我的应用程序随 Saxon 一起提供,应该使用 com.saxonica.config.EnterpriseTransformerFactory我有另一个不随 Saxon 一起提供的应用程序。一旦我设置了系统属性,我的其他应用程序就无法启动,因为没有 com.saxonica.config.EnterpriseTransformerFactory在其类路径上。所以这对我来说似乎不是一个选择。
  2. 我已经重构了 TransformerFactory.newInstance 的每个地方被调用并缓存 TransformerFactory。但我的依赖项中有很多地方无法重构代码。

我的问题是: 为什么 FactoryFinder 不重用 ServiceLoader?除了使用系统属性之外,还有其他方法可以加快整个 ServiceLoader 进程吗?难道不能在 JDK 中对此进行更改,以便 FactoryFinder 重用 ServiceLoader 实例吗?此外,这并不特定于单个 FactoryFinder。此行为对于 javax.xml 中的所有 FactoryFinder 类都是相同的。到目前为止我已经看过的包。

我正在使用 OpenJDK 8/11。我的应用程序部署在 Tomcat 9 实例中。

编辑:提供更多详细信息

以下是单个 XMLInputFactory.newInstance 调用的调用堆栈: enter image description here

使用最多资源的地方是 ServiceLoaders$LazyIterator.hasNextService 。此方法调用getResources在 ClassLoader 上读取 META-INF/services/javax.xml.stream.XMLInputFactory文件。每次仅该调用就需要大约 35 毫秒。

有没有办法指示 Tomcat 更好地缓存这些文件,以便更快地提供它们?

最佳答案

35 毫秒 听起来像是涉及磁盘访问时间,这表明操作系统缓存存在问题。

类路径上是否有任何目录/非 jar 条目可能会减慢速度。此外,如果资源不存在于检查的第一个位置。

如果您可以设置线程上下文类加载器,则可以覆盖

ClassLoader.getResource,可以通过配置(我已经多年没有接触过tomcat)或者只是Thread.setContextClassLoader.

关于java - FactoryFinder 性能/缓存不良,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58376695/

相关文章:

java - 按前 3 个字符进行流分组

java - SwaggerGen 枚举生成空数据类型

java - 使用 Httpclient 登录页面身份验证并导航到下一页

java - Android SSLContext 受信任的证书不适用于 API 8

java - 如何使用人工智能开发光学字符识别器?

java - Jetty 中的 ServiceLoader 问题

具有多个类加载器的 Java ServiceLoader

关于 serviceloader 的 Java 9 依赖问题

java - 什么时候使用 ServiceLoader 而不是像 OSGi 这样的东西

java - Android中的ServiceLoader "Couldn' t实例化类”