[java] JAXB 생성 컨텍스트 및 마샬 러 비용

질문은 약간 이론적입니다. JAXB 컨텍스트, 마샬 러 및 언 마샬 러를 만드는 데 드는 비용은 얼마입니까?

내 코드는 각 마샬링에서 컨텍스트와 마샬 러를 생성하는 것보다 모든 마샬링 작업에 대해 동일한 JAXB 컨텍스트 및 가능하면 동일한 마샬 러를 유지하는 것이 도움이 될 수 있음을 발견했습니다.

그렇다면 JAXB 컨텍스트와 마샬 러 / 언 마샬 러를 만드는 데 드는 비용은 얼마입니까? 각 마샬링 작업에 대해 컨텍스트 + 마샬 러를 만드는 것이 괜찮습니까? 아니면 피하는 것이 더 낫습니까?



답변

참고 : 저는 EclipseLink JAXB (MOXy) 리더이자 JAXB 2 ( JSR-222 ) 전문가 그룹의 구성원입니다.

JAXBContext스레드로부터 안전하며 메타 데이터를 여러 번 초기화하는 비용을 피하기 위해 한 번만 생성하고 재사용해야합니다. MarshallerUnmarshaller스레드에 안전하지만, 만들 경량 및 작업 당 생성 할 수 없습니다.


답변

이상적으로, 당신은 싱글이 있어야 JAXBContext하고 로컬 인스턴스 MarshallerUnmarshaller.

JAXBContext인스턴스는 스레드 안전 동안입니다 MarshallerUnmarshaller인스턴스가 없는 스레드 안전하고 스레드에서 공유해서는 안됩니다.


답변

이것이 javadoc에서 구체적으로 설명되지 않은 것은 유감입니다. 내가 말할 수있는 것은 Spring이 스레드간에 공유되는 전역 JAXBContext를 사용하는 반면, 각 마샬링 작업에 대해 새 마샬 러를 생성 하고 코드에 JAXB 마샬 러가 반드시 스레드로부터 안전하지 않다는 내용 의 javadoc 주석 을 생성한다는 것입니다.

이 페이지에서도 마찬가지입니다 : https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety .

JAXBContext를 생성하는 것은 주석에 대한 클래스와 패키지를 스캔하는 것과 관련이 있기 때문에 비용이 많이 드는 작업이라고 생각합니다. 그러나 그것을 측정하는 것이 가장 좋은 방법입니다.


답변

JAXB 2.2 ( JSR-222 )는 “4.2 JAXBContext”섹션에서 다음과 같이 말합니다.

JAXBContext인스턴스 생성과 관련된 오버 헤드를 피하기 위해 JAXB 애플리케이션은 JAXBContext 인스턴스를 재사용하는 것이 좋습니다 . 추상 클래스 JAXBContext의 구현은 스레드로부터 안전해야 하므로 애플리케이션의 여러 스레드가 동일한 JAXBContext 인스턴스를 공유 할 수 있습니다.

[..]

JAXBContext 클래스는 변경 불가능하므로 스레드로부터 안전하도록 설계되었습니다. JAXBContxt의 새 인스턴스를 만들 때 잠재적으로 발생할 수있는 동적 처리의 양을 고려할 때 JAXBContext 인스턴스를 스레드간에 공유하고 가능한 한 많이 재사용하여 애플리케이션 성능을 향상시키는 것이 좋습니다.

안타깝게도 사양은 Unmarshaller및의 스레드 안전성에 대해 어떠한 주장도하지 않습니다 Marshaller. 따라서 그렇지 않다고 가정하는 것이 가장 좋습니다.


답변

다음을 사용하여이 문제를 해결했습니다.

  • 공유 스레드 안전 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");
        }
    }
}


답변

더 좋습니다 !! 위 게시물의 좋은 솔루션을 기반으로 생성자에서 컨텍스트를 한 번만 만들고 클래스 대신 저장하십시오.

줄 바꾸기 :

  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로 떨어집니다.


답변

나는 보통 ThreadLocal수업 패턴으로 이와 같은 문제를 해결합니다 . 각 클래스에 대해 다른 마샬 러가 필요하다는 사실을 감안할 때이를 singleton-map 패턴 과 결합 할 수 있습니다 .

15 분의 작업 시간을 절약하기 위해. 다음은 Jaxb Marshallers 및 Unmarshallers를위한 스레드 안전 팩토리 구현을 따릅니다.

다음과 같이 인스턴스에 액세스 할 수 있습니다.

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;
  }
}