JAXB tạo ngữ cảnh và chi phí cho bộ điều phối


120

Câu hỏi mang tính lý thuyết một chút, chi phí tạo ngữ cảnh JAXB, bộ điều phối và bộ quản lý bộ điều khiển là bao nhiêu?

Tôi nhận thấy rằng mã của mình có thể được hưởng lợi từ việc giữ cùng một ngữ cảnh JAXB và có thể là cùng một bộ điều phối cho tất cả các hoạt động sắp xếp thay vì tạo ngữ cảnh và bộ điều phối trên mỗi bộ điều phối.

Vậy chi phí tạo ngữ cảnh JAXB và marshaller / unmarshaller là bao nhiêu? Có thể tạo ngữ cảnh + người điều khiển cho mỗi thao tác điều chỉnh không hay tốt hơn là tránh nó?

Câu trả lời:


244

Lưu ý: Tôi là trưởng nhóm EclipseLink JAXB (MOXy) và là thành viên của nhóm chuyên gia JAXB 2 ( JSR-222 ).

JAXBContextlà chuỗi an toàn và chỉ nên được tạo một lần và sử dụng lại để tránh chi phí khởi tạo siêu dữ liệu nhiều lần. MarshallerUnmarshallerkhông an toàn cho chuỗi, nhưng nhẹ để tạo và có thể được tạo cho mỗi hoạt động.


7
câu trả lời chính xác. Bây giờ tôi có thể tự tin dựa trên kinh nghiệm của bạn với tư cách là trưởng nhóm trên JAXB.
Vladimir

7
Tôi tin bạn, nhưng điều này có được tìm thấy ở đâu đó trong tài liệu không?
Hurda

3
Nó được ghi lại cho RI: jaxb.java.net/guide/Performance_and_thread_safety.html (nhưng không phải Moxy AFAIK)
Caoilte

39
Vui lòng chỉ định điều này trong Javadoc. Không thỏa đáng khi không có tài liệu về những khía cạnh quan trọng này.
Thomas W

6
Trong Javadoc không đề cập rằng JAXBContext là chuỗi an toàn. Và chỉ về mọi ví dụ về cách sử dụng JAXB, không đề cập đến việc nó phải được tạo một lần và chia sẻ trên các chuỗi. Kết quả là bây giờ tôi đang xóa một rò rỉ tài nguyên hú khác khỏi hệ thống đang hoạt động. Grrrrrrr.
Reg Whitton

42

Tốt nhất, bạn nên có một bản sao đơn JAXBContextvà cục bộ của MarshallerUnmarshaller.

JAXBContexttrường hợp là thread-an toàn trong khi MarshallerUnmarshallertrường hợp được không thread-safe và không bao giờ nên chia sẻ trên chủ đề.


cảm ơn vì câu trả lời. Thật không may, tôi chỉ phải chọn một câu trả lời :-)
Vladimir

15

Thật tiếc vì điều này không được mô tả cụ thể trong javadoc. Những gì tôi có thể nói là Spring sử dụng một JAXBContext toàn cầu, được chia sẻ giữa các luồng, trong khi nó tạo ra một bộ điều phối mới cho mỗi hoạt động điều phối, với một nhận xét javadoc trong mã nói rằng các bộ sao chép JAXB không nhất thiết phải an toàn cho luồng.

Điều tương tự cũng được nói trên trang này: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-mispose-topics-performance-and-thread-safety .

Tôi đoán rằng việc tạo JAXBContext là một hoạt động tốn kém, bởi vì nó liên quan đến việc quét các lớp và gói cho các chú thích. Nhưng đo lường nó là cách tốt nhất để biết.


Xin chào @JB, câu trả lời tuyệt vời, đặc biệt là nhận xét của bạn về việc đo lường và tại sao JAXBContext lại tốn kém.
Vladimir

1
Javadoc đã luôn yếu về các sự kiện quan trọng của vòng đời . Nó chắc chắn mang lại cho chúng ta sự lặp lại không đáng kể của các trình nhận & thiết lập thuộc tính, nhưng để biết cách / nơi lấy hoặc tạo một phiên bản, đột biến & an toàn luồng .. thì dường như hoàn toàn bỏ lỡ những yếu tố quan trọng nhất đó. Thở dài :)
Thomas W

4

JAXB 2.2 ( JSR-222 ) có điều này để nói, trong phần "4.2 JAXBContext":

Để tránh chi phí liên quan đến việc tạo một JAXBContextphiên bản, một ứng dụng JAXB được khuyến khích sử dụng lại một phiên bản JAXBContext . Việc triển khai lớp trừu tượng JAXBContext là bắt buộc để an toàn cho luồng , do đó, nhiều luồng trong một ứng dụng có thể chia sẻ cùng một cá thể JAXBContext.

[..]

Lớp JAXBContext được thiết kế để không thay đổi và do đó an toàn cho luồng. Với số lượng xử lý động có thể xảy ra khi tạo một phiên bản mới của JAXBContxt, chúng tôi khuyên bạn nên chia sẻ một phiên bản JAXBContext trên các luồng và được sử dụng lại càng nhiều càng tốt để cải thiện hiệu suất ứng dụng.

Thật không may, đặc điểm kỹ thuật không đưa ra bất kỳ tuyên bố nào liên quan đến an toàn luồng của UnmarshallerMarshaller. Vì vậy, tốt nhất là cho rằng chúng không phải vậy.


3

Tôi đã giải quyết vấn đề này bằng cách sử dụng:

  • chia sẻ chuỗi an toàn JAXBContextun / marschallers cục bộ chuỗi
  • (vì vậy về mặt lý thuyết, sẽ có càng nhiều trường hợp un / marshaller có các luồng đã truy cập chúng)
  • với sự đồng bộ hóa chỉ khi khởi tạo 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 sẽ giới thiệu các vấn đề phụ khác mà không có lợi ích. Chỉ cần giữ một JAXBContext duy nhất (đó là phần tốn kém) và tạo một Unmarshaller mới bất cứ khi nào được yêu cầu.
ymajoros

2

Thậm chí còn tốt hơn!! Dựa trên giải pháp tốt từ bài đăng ở trên, hãy tạo ngữ cảnh chỉ một lần trong hàm tạo và lưu nó thay vì lớp.

Thay thế dòng:

  private Class clazz;

với cái này:

  private JAXBContext jc;

Và hàm tạo chính với hàm này:

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

vì vậy trong getMarshaller / getUnmarshaller, bạn có thể xóa dòng này:

  JAXBContext jc = JAXBContext.newInstance(clazz);

Cải tiến này, trong trường hợp của tôi, thời gian xử lý giảm từ 60 ~ 70ms xuống chỉ 5 ~ 10ms


Tệp xml mà bạn đang phân tích cú pháp lớn như thế nào. Bạn có thấy sự cải thiện đáng kể với các tệp xml rất lớn không?
John

1
nó không thực sự là vấn đề của các tệp xml lớn (mỏ chỉ từ 2-3kb lên đến + 6mb), mà thay vào đó là một số lượng rất lớn các tệp xml (chúng ta đang nói ở đây khoảng 10.000 yêu cầu xml mỗi phút); trong trường hợp đó tạo ra bối cảnh chỉ một lần đạt được những ms chút làm cho một sự khác biệt rất lớn
tbarderas

1

Tôi thường giải quyết các vấn đề như thế này với một ThreadLocalmẫu lớp. Với thực tế là bạn cần một bộ điều phối khác nhau cho mỗi Lớp, bạn có thể kết hợp nó với một singletonmẫu-bản đồ.

Để tiết kiệm cho bạn 15 phút làm việc. Sau đây là cách tôi triển khai Nhà máy an toàn luồng cho Jaxb Marshallers và Unmarshallers.

Nó cho phép bạn truy cập các trường hợp như sau ...

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

Và mã bạn sẽ cần là một lớp Jaxb nhỏ trông như sau:

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 sẽ giới thiệu các vấn đề phụ khác mà không có lợi ích. Chỉ cần giữ một JAXBContext duy nhất (đó là phần tốn kém) và tạo một Unmarshaller mới bất cứ khi nào được yêu cầu.
ymajoros

Bạn thực sự không cần các JAXBContexts riêng biệt vì bạn có thể vượt qua nhiều lớp. Vì vậy, nếu bạn có thể dự đoán những lớp nào sẽ được điều chỉnh, bạn có thể tạo một lớp dùng chung duy nhất. Ngoài ra, thông số kỹ thuật JAXB yêu cầu chúng phải được bảo vệ luồng.
MauganRa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.