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


120

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

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

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

답변:


244

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

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


7
좋은 대답입니다. JAXB의 리더로서의 경험을 바탕으로 이제 확신 할 수 있습니다.
Vladimir

7
나는 당신을 신뢰하지만 이것은 문서의 어딘가에서 찾을 수 있습니까?
Hurda 2014 년


39
Javadoc에서이를 지정하십시오. 이러한 중요한 측면이 문서화되지 않은 것은 만족스럽지 않습니다.
Thomas W

6
JAXBContext가 스레드로부터 안전하다는 것은 Javadoc에 언급되어 있지 않습니다. 그리고 JAXB를 사용하는 방법에 대한 거의 모든 예제는 한 번 생성되고 스레드간에 공유되어야한다는 것을 언급하지 않습니다. 결과적으로 나는 이제 라이브 시스템에서 또 다른 하울링 리소스 누출을 제거하고 있습니다. Grrrrrrr.
Reg Whitton

42

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

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


답변 해주셔서 감사합니다. 불행하게도, 난 :-) 단 하나의 답을 선택해야합니다
블라디미르

15

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

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

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


안녕하세요 @JB, 특히 측정에 대한 귀하의 의견과 JAXBContext가 비용이 많이 드는 이유에 대한 훌륭한 답변입니다.
Vladimir

1
Javadoc은 항상 생명주기 의 중요한 사실에 약했습니다 . 속성 getter 및 setter의 사소한 반복을 제공하지만 인스턴스를 가져 오거나 생성하는 방법 / 어디에 대해 아는 것은 변이 및 스레드 안전성 .. 가장 중요한 요소를 완전히 놓친 것 같습니다. 한숨 :)
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 파일이 크게 개선 되었습니까?
John

1
실제로 큰 xml 파일의 문제가 아닙니다 (광산은 2-3kb에서 + 6mb까지). 대신 매우 많은 xml 파일의 문제입니다 (여기서는 분당 약 10,000 개의 xml 요청에 대해 이야기하고 있습니다). 한 번만 그 작은 밀리 확보 상황을 만드는 경우에 큰 차이가 있습니다
tbarderas

1

나는 보통 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;
  }
}

10
ThreadLocal은 이점없이 다른 하위 문제를 도입 할 것입니다. 단일 JAXBContext (비용이 많이 드는 부분)를 유지하고 필요할 때마다 새 Unmarshaller를 생성하십시오.
ymajoros 2014

여러 클래스를 전달할 수 있으므로 실제로 별도의 JAXBContext가 필요하지 않습니다. 따라서 마샬링 될 클래스를 예측할 수 있다면 하나의 공유 클래스를 만들 수 있습니다. 또한 JAXB 사양에서는 이미 스레드로부터 안전해야합니다.
MauganRa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.