JAXB bağlam oluşturma ve sıraya koyma maliyeti


120

Soru biraz teorik, JAXB bağlamı, düzenleyici ve unmarshaller oluşturmanın maliyeti nedir?

Kodumun, her sıralamada bağlam ve sıraya koyucu oluşturmaktan ziyade tüm sıralama işlemleri için aynı JAXB bağlamını ve muhtemelen aynı sıraya koyucuyu korumadan yararlanabileceğini buldum.

Peki, JAXB bağlamı ve marshaller / unmarshaller yaratmanın maliyeti nedir? Her sıralama operasyonu için bağlam + sıralayıcı oluşturmak doğru mu yoksa bundan kaçınmak daha mı iyi?

Yanıtlar:


244

Not: Ben EclipseLink JAXB (Moxy) kurşun ve JAXB 2 (üyesi JSR-222 ) uzman grubu.

JAXBContextiş parçacığı açısından güvenlidir ve yalnızca bir kez oluşturulmalı ve meta verileri birden çok kez başlatma maliyetinden kaçınmak için yeniden kullanılmalıdır. Marshallerve Unmarshalleriş parçacığı açısından güvenli değildir, ancak oluşturulması hafiftir ve işlem başına oluşturulabilir.


7
mükemmel cevap. JAXB'deki liderlik deneyiminize dayanarak şimdi kendime güvenebilirim.
Vladimir

7
Sana güveniyorum, ama bu belgelerde bir yerde bulunabilir mi?
Hurda

3
RI için belgelenmiştir: jaxb.java.net/guide/Performance_and_thread_safety.html (ancak Moxy AFAIK değil)
Caoilte

39
Lütfen bunu Javadoc'ta belirtin. Bu önemli yönlerin belgelenmemiş olması tatmin edici değil.
Thomas W

6
Javadoc'ta JAXBContext'in iş parçacığı için güvenli olduğu belirtilmemiştir. Ve JAXB'nin nasıl kullanılacağına dair hemen hemen her örnek, bir kez oluşturulması ve iş parçacıkları arasında paylaşılması gerektiğinden bahsetmiyor. Sonuç olarak, şimdi canlı bir sistemden bir başka uluyan kaynak sızıntısını kaldırıyorum. Grrrrrrr.
Reg Whitton

42

İdeal olarak, a tek olmalı JAXBContextve yerel örneklerini Marshallerve Unmarshaller.

JAXBContextörnekler iş parçacığı açısından güvenlidir Marshallerve Unmarshallerörnekler iş parçacığı açısından güvenli değildir ve hiçbir zaman evreler arasında paylaşılmamalıdır.


Cevap için teşekkürler. Maalesef sadece bir cevap seçmem gerekiyor :-)
Vladimir

15

Bunun javadoc'ta özellikle tanımlanmaması üzücü. Söyleyebileceğim şey, Spring'in iş parçacıkları arasında paylaşılan küresel bir JAXBContext kullandığı, ancak kodda JAXB sıralayıcılarının mutlaka iş parçacığı güvenli olmadığını söyleyen bir javadoc yorumu ile her sıralama işlemi için yeni bir sıraya koyucu oluşturduğu .

Bu sayfada da aynısı söyleniyor: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety .

Bir JAXBContext oluşturmanın maliyetli bir işlem olduğunu tahmin ediyorum, çünkü sınıfları ve ek açıklamalar için paketleri taramayı içeriyor. Ancak bunu ölçmek, bilmenin en iyi yoludur.


Merhaba @JB, özellikle ölçüm hakkındaki yorumlarınız ve JAXBContext'in neden maliyetli olduğu konusunda harika bir cevap.
Vladimir

1
Javadoc, yaşam döngüsünün önemli gerçeklerinde her zaman zayıf olmuştur . Kesinlikle bize mülk alıcıların ve ayarlayıcıların önemsiz bir tekrarını veriyor, ancak bir örneği nasıl / nereden elde edeceğimizi veya yaratacağımızı, mutasyon ve iş parçacığı güvenliğini bilmeye gelince ... bu en önemli faktörleri tamamen gözden kaçırıyor gibi görünüyor. İç çekme :)
Thomas W

4

JAXB 2.2 ( JSR-222 ), "4.2 JAXBContext" bölümünde şunu söyler:

Bir JAXBContextörnek oluşturmanın ek yükünü önlemek için , bir JAXB uygulamasının bir JAXBContext örneğini yeniden kullanması önerilir . Soyut sınıf JAXBContext uygulamasının iş parçacığı açısından güvenli olması gerekir , bu nedenle bir uygulamadaki birden çok iş parçacığı aynı JAXBContext örneğini paylaşabilir.

[..]

JAXBContext sınıfı, değişmez ve dolayısıyla iş parçacığı güvenli olacak şekilde tasarlanmıştır. Yeni bir JAXBContxt örneği oluştururken potansiyel olarak gerçekleşebilecek dinamik işlem miktarı göz önüne alındığında, bir JAXBContext örneğinin iş parçacıkları arasında paylaşılması ve uygulama performansını iyileştirmek için mümkün olduğunca yeniden kullanılması önerilir.

Ne yazık ki, şartname iplik-güvenliğiyle ilgili herhangi bir iddiada bulunmamaktadır Unmarshallerve Marshaller. Öyleyse öyle olmadığını varsaymak en iyisidir.


3

Bu sorunu kullanarak çözdüm:

  • paylaşılan iş parçacığı güvenli JAXBContext ve iş parçacığı yerel un / marschallers
  • (bu yüzden teorik olarak, onlara erişen iş parçacığı olduğu kadar çok un / marshaller örneği olacaktır )
  • yalnızca un / marshaller'ın başlatılmasında senkronizasyon ile .
public class MyClassConstructor {
    private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<Unmarshaller>() {
        protected synchronized Unmarshaller initialValue() {
            try {
                return jaxbContext.createUnmarshaller();
            } catch (JAXBException e) {
                throw new IllegalStateException("Unable to create unmarshaller");
            }
        }
    };
    private final ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<Marshaller>() {
        protected synchronized Marshaller initialValue() {
            try {
                return jaxbContext.createMarshaller();
            } catch (JAXBException e) {
                throw new IllegalStateException("Unable to create marshaller");
            }
        }
    };

    private final JAXBContext jaxbContext;

    private MyClassConstructor(){
        try {
            jaxbContext = JAXBContext.newInstance(Entity.class);
        } catch (JAXBException e) {
            throw new IllegalStateException("Unable to initialize");
        }
    }
}

8
ThreadLocal, herhangi bir fayda sağlamadan diğer ince sorunları da beraberinde getirecektir. Sadece tek bir JAXBContext (pahalı kısım budur) tutun ve gerektiğinde yeni bir Unmarshaller oluşturun.
ymajoros

2

Daha iyi!! Yukarıdaki gönderideki iyi çözüme dayanarak, bağlamı kurucuda yalnızca bir kez oluşturun ve sınıf yerine kaydedin.

Satırı değiştirin:

  private Class clazz;

Bununla birlikte:

  private JAXBContext jc;

Ve bununla ana kurucu:

  private Jaxb(Class clazz)
  {
     this.jc = JAXBContext.newInstance(clazz);
  }

böylece getMarshaller / getUnmarshaller'da bu satırı kaldırabilirsiniz:

  JAXBContext jc = JAXBContext.newInstance(clazz);

Bu iyileştirme, benim durumumda, işlem sürelerinin 60 ~ 70 ms'den sadece 5 ~ 10 ms'ye düşmesini sağlıyor.


Ayrıştırdığınız xml dosyası ne kadar büyüktü. Çok büyük xml dosyalarında önemli bir gelişme görüyor musunuz?
John

1
Bu gerçekten büyük xml dosyaları meselesi değil (madenler sadece 2-3kb'den + 6mb'ye kadar gidiyor), bunun yerine çok büyük sayıda xml dosyası meselesi (burada dakikada yaklaşık 10.000 xml isteğinden bahsediyoruz); bu durumda bağlamı oluşturmak sadece bir kez bu küçük
ms'leri

1

Genellikle bunun gibi problemleri ThreadLocalsınıf modeliyle çözerim. Her Sınıf için farklı bir görevlendiriciye ihtiyacınız olduğu gerçeği göz önüne alındığında, bunu bir singleton-map kalıbı ile birleştirebilirsiniz .

Size 15 dakika işten tasarruf etmek için. Burada, Jaxb Marshallers ve Unmarshallers için iş parçacığı güvenli bir Fabrika uygulamamı izler.

Örneklere aşağıdaki şekilde erişmenizi sağlar ...

Marshaller m = Jaxb.get(SomeClass.class).getMarshaller();
Unmarshaller um = Jaxb.get(SomeClass.class).getUnmarshaller();

Ve ihtiyacınız olacak kod, aşağıdaki gibi görünen küçük bir Jaxb sınıfıdır:

public class Jaxb
{
  // singleton pattern: one instance per class.
  private static Map<Class,Jaxb> singletonMap = new HashMap<>();
  private Class clazz;

  // thread-local pattern: one marshaller/unmarshaller instance per thread
  private ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<>();
  private ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<>();

  // The static singleton getter needs to be thread-safe too, 
  // so this method is marked as synchronized.
  public static synchronized Jaxb get(Class clazz)
  {
    Jaxb jaxb =  singletonMap.get(clazz);
    if (jaxb == null)
    {
      jaxb = new Jaxb(clazz);
      singletonMap.put(clazz, jaxb);
    }
    return jaxb;
  }

  // the constructor needs to be private, 
  // because all instances need to be created with the get method.
  private Jaxb(Class clazz)
  {
     this.clazz = clazz;
  }

  /**
   * Gets/Creates a marshaller (thread-safe)
   * @throws JAXBException
   */
  public Marshaller getMarshaller() throws JAXBException
  {
    Marshaller m = marshallerThreadLocal.get();
    if (m == null)
    {
      JAXBContext jc = JAXBContext.newInstance(clazz);
      m = jc.createMarshaller();
      marshallerThreadLocal.set(m);
    }
    return m;
  }

  /**
   * Gets/Creates an unmarshaller (thread-safe)
   * @throws JAXBException
   */
  public Unmarshaller getUnmarshaller() throws JAXBException
  {
    Unmarshaller um = unmarshallerThreadLocal.get();
    if (um == null)
    {
      JAXBContext jc = JAXBContext.newInstance(clazz);
      um = jc.createUnmarshaller();
      unmarshallerThreadLocal.set(um);
    }
    return um;
  }
}

10
ThreadLocal, herhangi bir fayda sağlamadan diğer ince sorunları da beraberinde getirecektir. Sadece tek bir JAXBContext (pahalı kısım budur) tutun ve gerektiğinde yeni bir Unmarshaller oluşturun.
ymajoros

Birden çok sınıfta geçebileceğiniz için aslında ayrı JAXBContexts'e ihtiyacınız yoktur. Yani, hangi sınıfların sıralanacağını tahmin edebiliyorsanız, tek bir paylaşılan tane oluşturabilirsiniz. Ayrıca, JAXB özelliği, bunların zaten iş parçacığı güvenli olmasını gerektirir.
MauganRa
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.