FactoryFinder performansı / kötü önbellekleme


9

Ben xml işleme çok yapıyor büyük bir sınıf yolu ile oldukça büyük bir java ee uygulama var. Şu anda bazı işlevleri hızlandırmak ve örnekleme profilers örnekleme yoluyla yavaş kod yolları bulma çalışıyorum.

Fark ettiğim bir şey, özellikle kodumuzun çağrı yaptığımız TransformerFactory.newInstance(...)bölümlerinin umutsuzca yavaş olmasıdır. Bunu her zaman yeni bir örnek oluşturma FactoryFinderyöntemine kadar izledim . Gelen javadoc ben önbelleğe alma hakkında aşağıdaki not buldum:findServiceProviderServiceLoaderServiceLoader

Tedarikçiler tembel olarak, yani talep üzerine yerleştirilir ve somutlaştırılır. Bir hizmet yükleyici, şu ana kadar yüklenmiş olan sağlayıcıların önbelleğini tutar. Yineleyici yönteminin her çağrılması, önce önbelleğin tüm öğelerini örnekleme sırasında veren bir yineleyici döndürür ve ardından kalan sağlayıcıları tembel olarak bulur ve her birini sırayla önbelleğe ekler. Önbellek yeniden yükleme yöntemi ile temizlenebilir.

Çok uzak çok iyi. Bu OpenJDKs FactoryFinder#findServiceProvideryönteminin bir parçasıdır :

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) {
            ...
        }
    }

Yapılan her çağrı findServiceProvideraramalar ServiceLoader.load. Bu, her seferinde yeni bir ServiceLoader oluşturur . Bu şekilde, ServiceLoaders önbellekleme mekanizmasının hiç kullanılmadığı görülmektedir. Her çağrı, istenen ServiceProvider için sınıfyolunu tarar.

Zaten denedim:

  1. javax.xml.transform.TransformerFactoryBelirli bir uygulamayı belirtmek gibi bir sistem özelliği ayarlayabileceğinizi biliyorum . Bu şekilde FactoryFinder ServiceLoader işlemini ve süper hızını kullanmaz. Ne yazık ki bu bir jvm geniş özellik ve benim jvm çalışan diğer java süreçlerini etkiler. Örneğin benim uygulamam Saxon ile birlikte geliyor ve kullanmalıyım com.saxonica.config.EnterpriseTransformerFactorySaxon ile gönderilmeyen başka bir uygulamam var. System özelliğini ayarlar ayarlamaz diğer uygulamam başlatılamıyor çünkü com.saxonica.config.EnterpriseTransformerFactorysınıf yolunda yok. Yani bu benim için bir seçenek gibi görünmüyor.
  2. Ben zaten a TransformerFactory.newInstancedenilen her yeri yeniden düzenledim ve TransformerFactory önbellek. Ancak bağımlılıklarımda kodu yeniden düzenleyemediğim çeşitli yerler var.

Sorularım: FactoryFinder neden bir ServiceLoader'ı yeniden kullanmıyor? Sistem özelliklerini kullanmak dışında tüm ServiceLoader işlemini hızlandırmanın bir yolu var mı? Bu JDK'da değiştirilemez, böylece bir FactoryFinder bir ServiceLoader örneğini yeniden kullanır mı? Ayrıca bu, tek bir FactoryFinder'a özgü değildir. Bu bahaviour javax.xml, şimdiye kadar baktığım paketteki tüm FactoryFinder sınıfları için aynı .

OpenJDK 8/11 kullanıyorum. Uygulamalarım bir Tomcat 9 örneğinde dağıtıldı.

Düzenle: Daha fazla ayrıntı sağlama

Tek bir XMLInputFactory.newInstance çağrısı için çağrı yığını şöyledir: resim açıklamasını buraya girin

Kaynakların çoğunun kullanıldığı yerler ServiceLoaders$LazyIterator.hasNextService. Bu yöntem dosyayı getResourcesokumak için ClassLoader'ı çağırır META-INF/services/javax.xml.stream.XMLInputFactory. Sadece bu çağrı her seferinde yaklaşık 35 ms sürer.

Tomcat'e daha hızlı sunulmaları için bu dosyaları daha iyi önbelleğe almaları talimatı vermenin bir yolu var mı?


FactoryFinder.java değerlendirmenizi kabul ediyorum. ServiceLoader'ı önbelleğe alması gerektiği anlaşılıyor. Openjdk kaynağını indirip oluşturmayı denediniz mi? Kulağa büyük bir görev gibi geldiğini biliyorum ama olmayabilir. Ayrıca, FactoryFinder.java'ya karşı bir sorun yazmak ve birisinin sorunu alıp bir çözüm sunup sunmadığını görmek faydalı olabilir.
djhallx

İşleminize -Dbayrak kullanarak özellik ayarlamayı denediniz Tomcatmi? Örneğin: -Djavax.xml.transform.TransformerFactory=<factory class>.Diğer uygulamaların özelliklerini geçersiz kılmamalıdır. Yayınınız iyi tanımlanmış ve muhtemelen denediniz, ancak onaylamak istiyorum. Bkz Javax.xml.transform.TransformerFactory sistem özelliği nasıl ayarlanır , nasıl Tomcat HeapMemory veya JVM değişkenleri ayarlamak için
Michal Ziober

Yanıtlar:


1

35 ms disk erişim süreleri varmış gibi geliyor ve bu da OS önbelleği ile ilgili bir soruna işaret ediyor.

Sınıf yolunda işleri yavaşlatabilecek herhangi bir dizin / jar olmayan girişler varsa. Ayrıca, kaynak denetlenen ilk konumda yoksa.

ClassLoader.getResourceiş parçacığı bağlam sınıfı yükleyiciyi, yapılandırma (yıllardır tomcat'e dokunmadım) yoluyla veya sadece ayarlayabilirseniz geçersiz kılınabilir Thread.setContextClassLoader.


Bu işe yarayabilir gibi geliyor. Er ya da geç buna bir göz atacağım. Teşekkür ederim!
Wagner Michael

1

Bu hata ayıklamak için 30 dakika daha alabilir ve Tomcat'in Kaynak Önbellekleme'yi nasıl yaptığına baktım.

Özellikle CachedResource.validateResources(yukarıdaki alev grafiğinde bulunabilir) benim için ilgi çekiciydi. Bu döndürür trueeğer CachedResourcehala geçerli:

protected boolean validateResources(boolean useClassLoaderResources) {
        long now = System.currentTimeMillis();
        if (this.webResources == null) {
            ...
        }

        // TTL check here!!
        if (now < this.nextCheck) {
            return true;
        } else if (this.root.isPackedWarFile()) {
            this.nextCheck = this.ttl + now;
            return true;
        } else {
            return false;
        }
    }

Bir CachedResource'un aslında yaşamak için bir zamanı var gibi görünüyor (ttl). Tomcat'te cacheTtl'yi yapılandırmanın bir yolu var, ancak yalnızca bu değeri artırabilirsiniz. Kaynak önbellek yapılandırması gerçekten kolay görünmüyor.

Bu yüzden Tomcat'im varsayılan 5000 ms yapılandırılmış değerine sahip. Bu, performans testi yaparken beni kandırdı çünkü taleplerim arasında (grafiklere ve şeylere bakarken) 5 saniyeden biraz fazla zaman geçirdim. Bu yüzden tüm taleplerim temelde önbellek olmadan çalıştırıldı ve ZipFile.openher seferinde bu ağır tetiklendi .

Bu yüzden Tomcat yapılandırmasıyla gerçekten çok tecrübeli olmadığım için, burada doğru çözümün ne olduğundan emin değilim. CacheTTL'nin artırılması önbellekleri daha uzun tutar, ancak uzun vadede sorunu çözmez.

özet

Bence burada aslında iki suçlu var.

  1. FactoryFinder sınıfları ServiceLoader'ı yeniden kullanmaz. Onları yeniden kullanmamalarının geçerli bir nedeni olabilir - bir tanesini gerçekten düşünemiyorum.

  2. Tomcat web uygulaması kaynağı için sabit bir süre sonra önbellek çıkarma (sınıf yolundaki dosyalar - bir ServiceLoaderyapılandırma gibi )

Bunu ServiceLoader sınıfı için Sistem Özelliğini tanımlamamayla birleştirin ve her cacheTtlsaniye yavaş bir FactoryFinder çağrısı alırsınız .

Şimdilik cacheTtl'i daha uzun süre artırarak yaşayabilirim. Tom Hawtins'in Classloader.getResourcesbu performans darboğazından kurtulmanın sert bir yolu olduğunu düşünmeme rağmen geçersiz kılma önerisine de bakabilirim . Yine de bakmaya değer olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.