JAXB สร้างบริบทและต้นทุนของมาร์แชลเลอร์


120

คำถามเป็นเรื่องเชิงทฤษฎีค่าใช้จ่ายในการสร้างบริบท JAXB, marshaller และ unmarshaller คืออะไร?

ฉันพบว่ารหัสของฉันอาจได้รับประโยชน์จากการรักษาบริบท JAXB เดียวกันและอาจเป็นจอมพลเดียวกันสำหรับการดำเนินการมาร์แชลทั้งหมดแทนที่จะสร้างบริบทและมาร์แชลเลอร์ในแต่ละมาร์แชล

ดังนั้นค่าใช้จ่ายในการสร้างบริบท JAXB และ marshaller / unmarshaller คืออะไร? สามารถสร้างบริบท + จอมพลสำหรับการดำเนินการมาร์แชลแต่ละครั้งได้หรือไม่หรือควรหลีกเลี่ยง

คำตอบ:


244

หมายเหตุ: ฉันเป็นผู้นำEclipseLink JAXB (MOXy)และเป็นสมาชิกของ JAXB 2 ( JSR-222กลุ่มผู้เชี่ยวชาญ )

JAXBContextเธรดปลอดภัยและควรสร้างเพียงครั้งเดียวและใช้ซ้ำเพื่อหลีกเลี่ยงค่าใช้จ่ายในการเริ่มต้นข้อมูลเมตาหลายครั้ง MarshallerและUnmarshallerไม่ปลอดภัยต่อเธรด แต่มีน้ำหนักเบาในการสร้างและสามารถสร้างได้ต่อการดำเนินการ


7
คำตอบที่ดี ตอนนี้ฉันมั่นใจได้จากประสบการณ์ของคุณในฐานะหัวหน้าทีม JAXB
Vladimir

7
ฉันเชื่อใจคุณ แต่จะมีอยู่ในเอกสารหรือไม่?
Hurda

3
เอกสารนี้มีไว้สำหรับ RI: jaxb.java.net/guide/Performance_and_thread_safety.html (แต่ไม่ใช่ Moxy AFAIK)
Caoilte

39
โปรดระบุสิ่งนี้ใน Javadoc ไม่น่าพอใจที่จะมีประเด็นสำคัญเหล่านี้ที่ไม่มีเอกสารประกอบ
Thomas W

6
ไม่มีการกล่าวถึงใน Javadoc ว่า JAXBContext เป็นเธรดที่ปลอดภัย และในทุกๆตัวอย่างของวิธีการใช้ JAXB นั้นไม่ต้องพูดถึงว่าควรสร้างครั้งเดียวและแชร์ข้ามเธรด ด้วยเหตุนี้ฉันจึงลบการรั่วไหลของทรัพยากรอีกรายการหนึ่งออกจากระบบถ่ายทอดสด Grrrrrrr
Reg Whitton

42

ตามหลักการแล้วคุณควรมีซิงเกิลตันJAXBContextและอินสแตนซ์เฉพาะของMarshallerและUnmarshallerและ

JAXBContextอินสแตนซ์นั้นปลอดภัยต่อเธรดในขณะที่MarshallerและUnmarshallerอินสแตนซ์ไม่ปลอดภัยต่อเธรดและไม่ควรแชร์ข้ามเธรด


ขอบคุณสำหรับคำตอบ. น่าเสียดายที่ฉันต้องเลือกเพียงคำตอบเดียว :-)
Vladimir

15

เป็นที่น่าเสียดายที่สิ่งนี้ไม่ได้อธิบายไว้เป็นพิเศษใน javadoc สิ่งที่ฉันสามารถบอกได้ก็คือ Spring ใช้ JAXBontext ส่วนกลางซึ่งใช้ร่วมกันระหว่างเธรดในขณะที่สร้างมาร์แชลเลอร์ใหม่สำหรับการดำเนินการมาร์แชลแต่ละครั้งโดยมีข้อคิดเห็นของ javadocในรหัสที่บอกว่า JAXB marshallers ไม่จำเป็นต้องปลอดภัยต่อเธรด

เป็นเหมือนกันกล่าวว่าในหน้านี้: https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety

ฉันเดาว่าการสร้าง JAXBContext เป็นการดำเนินการที่มีค่าใช้จ่ายสูงเนื่องจากต้องใช้การสแกนคลาสและแพ็คเกจสำหรับคำอธิบายประกอบ แต่การวัดผลเป็นวิธีที่ดีที่สุดที่จะรู้


สวัสดี @JB คำตอบที่ดีโดยเฉพาะความคิดเห็นของคุณเกี่ยวกับการวัดผลและเหตุใด JAXBContext จึงมีราคาแพง
Vladimir

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

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 ต่อนาที) ในกรณีนั้นการสร้างบริบทเพียงครั้งเดียวที่ได้รับ ms เล็ก ๆ เหล่านั้นสร้างความแตกต่างอย่างมาก
tbarderas

1

ฉันมักจะแก้ปัญหาเช่นนี้ด้วยThreadLocalรูปแบบชั้นเรียน เนื่องจากคุณต้องการมาร์แชลsingletonเลอร์ที่แตกต่างกันสำหรับแต่ละคลาสคุณสามารถรวมเข้ากับรูปแบบแผนที่

เพื่อช่วยคุณประหยัดเวลา 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

จริงๆแล้วคุณไม่จำเป็นต้องมี JAXBContexts แยกกันเนื่องจากคุณสามารถผ่านหลายคลาสได้ ดังนั้นหากคุณสามารถคาดเดาได้ว่าชั้นเรียนใดจะถูกจัดให้เป็นแบบมาร์แชลคุณสามารถสร้างคลาสที่ใช้ร่วมกันได้ นอกจากนี้ข้อมูลจำเพาะของ JAXB ต้องการให้เธรดปลอดภัยอยู่แล้ว
MauganRa
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.