JAXB创建上下文和封送处理程序的成本


120

这个问题有点理论化,创建JAXB上下文,封送和拆封的成本是多少?

我发现我的代码可以受益于为所有封送处理操作保留相同的JAXB上下文和可能相同的封送处理程序,而不是在每个封送处理中创建上下文和封送处理程序。

那么创建JAXB上下文和编组器/解组器的成本是多少?是否可以为每个封送处理操作创建context + marshaller,还是最好避免这种情况?

Answers:


244

注意: 我是EclipseLink JAXB(MOXy)的负责人,并且是JAXB 2(JSR-222)专家组的成员。

JAXBContext是线程安全的,因此只能创建一次并重用,以避免多次初始化元数据的开销。 Marshaller并且Unmarshaller不是线程安全的,但是创建起来很轻巧,可以按操作创建。


7
好答案。根据您担任JAXB主管的经验,我现在可以信心十足。
弗拉基米尔

7
我相信您,但是可以在文档中找到它吗?
Hurda 2014年

3
它已针对RI进行了记录:jaxb.java.net/guide/Performance_and_thread_safety.html(但未提供Moxy AFAIK)
Caoilte 2014年

39
请在Javadoc中指定。没有记录这些关键方面并不令人满意。
Thomas W

6
Javadoc中没有提到JAXBContext是线程安全的。几乎每个有关如何使用JAXB的示例都没有提及它应该创建一次并在线程之间共享。结果,我现在要从实时系统中消除另一个啸叫资源泄漏。rr
Reg Whitton

42

理想情况下,你应该有一个单身JAXBContext和本地实例MarshallerUnmarshaller

JAXBContext实例是线程安全的,MarshallerUnmarshaller实例不是线程安全的,并且永远不要在线程之间共享。


感谢你的回答。不幸的是,我只选择一个答案:-)
Vladimir

15

遗憾的是,javadoc中没有对此进行详细描述。我可以说的是,Spring使用了在线程之间共享的全局JAXBContext,而它为每个编组操作创建了一个新的编组器,并且在代码中有一个Javadoc注释,指出JAXB编组器不一定是线程安全的。

在此页面上也是如此:https : //javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety

我猜想创建JAXBContext是一项昂贵的操作,因为它涉及扫描类和包以获取注释。但是测量它是了解的最佳方法。


@JB,您好,特别是您对测量的评论以及JAXBContext为何昂贵的一个好答案。
弗拉基米尔

1
Javadoc在生命周期的关键事实上一直很薄弱。它的确可以使我们轻松重复属性获取器和设置器的操作,但是对于知道如何/在何处获取或创建实例,变异和线程安全性……似乎完全错过了那些最重要的因素。感叹:)
Thomas W

4

JAXB 2.2(JSR-222)在“ 4.2 JAXBContext”部分中有这样的说法:

为了避免创建JAXBContext实例所涉及的开销,鼓励 JAXB应用程序重用JAXBContext实例。抽象类JAXBContext的实现必须是线程安全的,因此,应用程序中的多个线程可以共享同一JAXBContext实例。

[..]

JAXBContext类被设计为不可变的,因此是线程安全的。考虑到创建新的JAXBContxt实例时可能发生的动态处理量,建议在线程之间共享JAXBContext实例,并尽可能重用它,以提高应用程序性能。

不幸的是,该规范没有对Unmarshaller和的线程安全性提出任何要求Marshaller。因此,最好假设它们不是。


3

我使用以下方法解决了这个问题:

  • 共享线程安全JAXBContext和线程本地un / marschallers
  • (因此从理论上讲,un / marshaller实例的数量将与访问它们的线程数量一样多)
  • 仅在un / marshaller的初始化上进行同步。
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将带来其他细微的问题,但没有任何好处。只需保留一个JAXBContext(这是昂贵的部分),并在需要时创建一个新的Unmarshaller。
ymajoros 2014年

2

更好!!基于以上文章的好的解决方案,在构造函数中一次创建上下文,然后保存它而不是类。

替换行:

  private Class clazz;

与此:

  private JAXBContext jc;

以及与此相关的主要构造函数:

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

因此,在getMarshaller / getUnmarshaller中,您可以删除以下行:

  JAXBContext jc = JAXBContext.newInstance(clazz);

就我而言,这种改进使得处理时间从60〜70ms减少到5〜10ms


您正在解析的xml文件有多大。您看到非常大的xml文件有明显的改进吗?
约翰

1
这实际上不是大的xml文件的问题(地雷从2-3kb到+ 6mb),而是大量的xml文件的问题(我们在这里谈论的是每分钟10,000个xml请求);在那种情况下,创建上下文仅获得这些小毫秒就产生了巨大的变化
tbarderas

1

我通常使用ThreadLocal类模式解决此类问题。考虑到每个类都需要不同的编组器,可以将其与singleton-map模式结合使用。

为您节省15分钟的时间。在此之后,我实现了针对Jaxb Marshallers和Unmarshallers的线程安全Factory。

它允许您按以下方式访问实例...

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

您将需要的代码是一个小Jaxb类,如下所示:

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将带来其他细微的问题,但没有任何好处。只需保留一个JAXBContext(这是昂贵的部分),并在需要时创建一个新的Unmarshaller。
ymajoros 2014年

实际上,您不需要单独的JAXBContext,因为您可以传递多个类。因此,如果您可以预测将编组哪些类,则可以创建一个共享的类。同样,JAXB规范要求它们已经是线程安全的。
MauganRa
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.