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


120

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

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

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

답변:


244

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

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


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

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


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

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

42

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

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


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

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.