多くのxml処理を実行する巨大なクラスパスを備えた、かなり大きなjava eeアプリケーションを持っています。現在、一部の機能を高速化し、サンプリングプロファイラーを介して遅いコードパスを見つけようとしています。
私が気づいたことの1つは、特に呼び出しのあるコードの一部TransformerFactory.newInstance(...)
が必死に遅いことです。私はこれを常に新しいインスタンスを作成するFactoryFinder
メソッドまで追跡しfindServiceProvider
ましたServiceLoader
。でServiceLoader
javadocの私は、キャッシングについては、以下の注記が見つかりました:
プロバイダーは、遅延して、つまりオンデマンドで配置およびインスタンス化されます。サービスローダーは、これまでにロードされたプロバイダーのキャッシュを維持します。イテレータメソッドを呼び出すたびに、インスタンス化の順序で最初にキャッシュのすべての要素を生成するイテレータが返されます。次に、残りのプロバイダを遅延検索してインスタンス化し、それぞれを順番にキャッシュに追加します。キャッシュはreloadメソッドでクリアできます。
ここまでは順調ですね。これはOpenJDKs 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に付属しており、Saxonに付属しcom.saxonica.config.EnterpriseTransformerFactory
ていない別のアプリケーションを使用する必要があります。システムプロパティを設定するとすぐに、他のアプリケーションが起動しませんcom.saxonica.config.EnterpriseTransformerFactory
。そのクラスパスに何もないためです。したがって、これは私の選択肢ではないようです。- a
TransformerFactory.newInstance
が呼び出され、TransformerFactoryをキャッシュするすべての場所をすでにリファクタリングしています。しかし、依存関係にはコードをリファクタリングできないさまざまな場所があります。
私の質問は、FactoryFinderがServiceLoaderを再利用しないのはなぜですか?システムプロパティを使用する以外に、このServiceLoaderプロセス全体を高速化する方法はありますか?FactoryFinderがServiceLoaderインスタンスを再利用するように、これをJDKで変更できませんでしたか?また、これは単一のFactoryFinderに固有のものではありません。この動作は、これまでに確認したjavax.xml
パッケージ内のすべてのFactoryFinderクラスで同じです。
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システムプロパティを設定する方法、Tomcatの中HeapMemoryまたはJVM引数を設定する方法