2017-12-04 6 views
1

Ich habe die Zeit Kosten von Unmarshalling XML zu Objekten mit Jaxb2 mit einer großen (1,7 MB) XML-Payload mit etwas langen (48 Zeichen) Tag-Namen gemessen. Ich beobachtete via JProfiler im Sampling-Modus, dass String-Interning-Arbeit einen festen Teil der Zeit verbrachte.Das Deaktivieren des String-Internings innerhalb von Jaxb2 beschleunigt das Zurückmarshalieren von einem Fastinfoset-Stream. Warum ist es so schwer zu deaktivieren?

Ich habe einige Nachforschungen gemacht und festgestellt, dass Jaxb in einem Modus betrieben werden kann, in dem es keine Strings sperrt. Meine Theorie war, dass in einigen Fällen die Nicht-Internierung von Zeichenketten beim Unmarshalling die Leistung auf Kosten der Verwendung von mehr Heap-Speicher verbessern konnte, da nicht jeder Tag-Name-String während des Interning-Prozesses durchsucht werden musste.

Die Methode, die ich verwendet, um jaxb der Internierung Verhalten unterdrücken war auf dem Fastinfoset „StAXDocumentParser“, um den „org.codehaus.stax2.internNames“ und „org.codehaus.stax2.internNsUris“ Eigenschaften (die XMLStreamReader implementiert). Es ist mir nicht 100% klar, warum Sie diese auf "true" setzen müssen, um zu verhindern, dass Jaxb Strings interniert, aber so funktioniert es.

Diese JUnit getriebene Tests sind, was ich, dass das Deaktivieren jaxb String interning Verhalten einen großen Unterschied in der Leistung macht den Abschluss verwendet:

https://github.com/gjd6640/fastinfoset-performance-evaluation

Also meine Frage ist mehrteilig:

1) Missverstehe ich etwas Wichtiges und sollte ich nicht versuchen, Jaxbs String-Interning-Verhalten zu deaktivieren?

2) Gibt es eine bessere Möglichkeit, Jaxb nicht zu internen Strings zu leiten? Mit der Klasse "StAXManager" können Sie diese Woodstox-orientierten Eigenschaften nicht festlegen. Für diesen Test habe ich den StAXManager wie unten gezeigt erweitert, um das Problem zu umgehen. Dies ist ein Hack, den ich in der Produktion nicht verwenden möchte. Ich vermute, dass die Idee hier ist, dass, wenn Jaxb von einem Woodstox-Stream entpackt wird, er sieht, ob Woodstox bereits interniert und wenn "Ja" reagiert, reagiert Jaxb, indem er diesen Schritt des Prozesses deaktiviert. Ich betrüge, indem ich diese Logik in der Jaxb-Bibliothek piggybacke, also möchte ich einen besseren Weg, dies zu tun.

package com.sun.xml.fastinfoset.stax; 
public class JaxbStringInternSuppressionStaxManager extends StAXManager { 
    public JaxbStringInternSuppressionStaxManager() { 
     // Add to the allowable list of feature names so that the user may set these "StAXInputFactory" properties 
     super.features.put("org.codehaus.stax2.internNames", null); 
     super.features.put("org.codehaus.stax2.internNsUris", null); 
    } 
} 

Update:

Wie üblich, "Eine Frage gut gestellt ist halb beantwortet". Ich habe gerade beim Verfassen dieser Frage bemerkt, dass "com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector" überprüft, ob die Klasse "com.sun.xml. internal .rastinfoset.stax.StAXDocumentParser" "ist vom verwendeten XMLStreamReader zuweisbar und aktiviert String-Interning nicht. In meinem Fall ist mein Stream-Objekt ein "com.sun.xml.fastinfoset.stax.StAXDocumentParser", so dass das Internieren nicht deaktiviert wird. Nun stellt sich die Frage: "Warum tut es das nur für den internen Geschmack der Fastinfoset-Bibliothek?" Vielleicht finde ich die Antwort, indem ich this post sorgfältig lese.

Wenn es ein besseres Forum für diese Art von Frage gibt, wie zum Beispiel eine aktive Entwickler-Benutzergruppe, teilen Sie bitte diese Informationen mit und ich werde sie über diesen Beitrag verlinken, damit die richtigen Leute diese Frage sehen.

+0

Addendum: Ich benutze JDK: 64-Bit jdk1.8.0_121, die scheint, bündeln Klassen von Oracle "com.sun.xml.bind: jaxb-impl" Bibliothek Version "2.1-b02-fcs". –

Antwort

1

Ich würde nicht unbedingt einem Profiler oder einem Test vertrauen, ohne auch den realen Anwendungsfall mit und ohne Internierung zu messen, also sei ein bisschen skeptisch. Es gibt jedoch einige Probleme mit Praktikanten. Insbesondere verwendet es eine Poolgröße mit fester Größe. Wenn der Pool also voll ist, verschlechtert sich die konstante Performance für Hash-Lookups zur Suche nach verknüpften Listen. Eine längere Diskussion finden Sie unter http://java-performance.info/string-intern-in-java-6-7-8/.

Kurz gesagt können Sie versuchen, die Poolgröße mit -XX:StringTableSize=n zu ändern (wobei n ideal Primzahl sein sollte) und sehen, was passiert.

Verwenden -XX:+PrintStringTableStatistics zu sehen, wie der Pool wurde verwendet, wenn das Programm beendet und verschiedene Größen versuchen.

EDIT: Dies war ein Versuch, zu beantworten, „gibt es eine bessere Art und Weise“ (das heißt intern macht schneller). Ich überlasse die andere Frage jemandem, der besser qualifiziert ist.

+0

Das ist faszinierendes Zeug. Danke für die Information. Ich habe kurz versucht, StringTableSize zuerst um etwa 2x und dann um etwa 100x zu erhöhen und habe keine Verbesserung gesehen. Basierend auf den Statistiken, die ich sehe, sieht es so aus, als ob mein Test nur 3090 der JVM-Standardhashmap-Größe von 60k verwendet: StringTable-Statistik: Anzahl der Buckets: 60013 = 480104 Bytes, Durchschnitt 8.000 Anzahl der Einträge: 3090 = 74160 Bytes, Durchschnitt 24.000 Anzahl der Literale: 3090 = 256072 Bytes, Durchschnitt 82.871 Gesamtfläche: = 810336 Bytes –

0

Lösung Option 1: Einfacher Ansatz, der die gesamte App auf eine andere jaxb Implementierung

Pull in jaxb-impl tauscht eine Version von jaxb zu verwenden, die mit dieser Fastinfoset Bibliothek besser abschneidet:

<!-- Both of these libs must be here in order to get performant behavior out of Jaxb by default. 
--> 
<dependency> 
     <groupId>com.sun.xml.fastinfoset</groupId> 
     <artifactId>FastInfoset</artifactId> 
     <version>1.2.13</version> 
     <scope>compile</scope> 
</dependency> 
<dependency> <!-- This artifactId also exists under javax.xml.bind but it appears that nobody uses that one... --> 
    <groupId>javax.xml</groupId> 
    <artifactId>jaxb-impl</artifactId> 
    <version>2.1</version> 
    <scope>runtime</scope> 
</dependency> 
<!-- End: Both of these libs... --> 

Dies hat den Nebeneffekt der Aktualisierung der Jaxb-Version, die vom Rest Ihres Codes verwendet wird. In einigen Situationen mag nicht wünschenswert sein. Wenn Sie beispielsweise eine gemeinsam genutzte Bibliothek erstellen, die in verschiedenen Apps verwendet werden kann, ist es unhöflich, diese Funktionalität zu ändern, wenn sie Ihre freigegebene Komponente einzieht.

Lösung Option 2: Die jaxb Implementierung und eine Performance-Hack JVM Verwenden Sie es in vertrauensvollen Trick, dass die Saiten bereits interniert (komplexer zu implementieren)

  • Use "Maven-Schatten-Plugin" schattieren und packe die Klassen der Fastinfoset-Bibliothek neu. Das Ergebnis sollte eine logikfreie Maven-Komponente sein. Dies ist optional und soll sicherstellen, dass Personen, die Ihre Fastinfoset-Codec-Komponente verwenden, keine Klassenpfadkollisionen aufgrund von transitiven Abhängigkeiten haben, die von Ihrer Codec-Bibliothek übernommen werden.
  • erstellen my-fastinfoset-Codec-Bibliothek, die eine einfache API bietet Fastinfoset Nutzlasten zu kodieren und zu dekodieren (man denke an Inputstreams und OutputStreams für Argumente und XMLStreamReader für den Rückgabetyp des Decoders verwenden). Fügen Sie eine Abhängigkeit von Ihrer neu gepackten Fastinfoset-Bibliothek hinzu. Beachten Sie, dass wenn Sie Eclipse verwenden, es nicht gut mit schattierten Bibliotheken umgehen kann, wenn m2e's "Workspace Resolution" aktiviert ist, deaktivieren Sie das für Ihr Codec-Projekt.
  • In dem my-fastinfoset-Codec eine Klasse, die die neu verpackt Fastinfoset Bibliothek "StAXManager" erstreckt. Diese Klasse sollte es erleichtern, die Eigenschaften festzulegen, die jaxb mitteilen, dass der XMLStreamReader, den sie erhalten hat, bereits die Zeichenfolgen für NS- und Tag-Namen interniert hat. Beispiel ist unten:
 
    package myrepackagedfastinfosetclassespackageprefix.shaded.com.sun.xml.fastinfoset.stax; 
    import myrepackagedfastinfosetclassespackageprefix.shaded.com.sun.xml.fastinfoset.stax.StAXManager; 
    public class JaxbStringInternSuppressionStaxManager extends StAXManager { 
     public JaxbStringInternSuppressionStaxManager() { 
      // Add to the allowable list of feature names so that the user may set these "StAXInputFactory" properties 
      super.features.put("org.codehaus.stax2.internNames", null); 
      super.features.put("org.codehaus.stax2.internNsUris", null); 
     } 

     /** 
     * This is an optimization. The FastInfoset libraries already intern strings and the JVM's jaxb implementation by default 
     * unnecessarily repeats that work. This is true at least for the 64 bit version of jdk1.8.0_121. 
     * 
     * The way that this workaround works is by piggybacking on a Jaxb optimization for the Woodstox parser. When we set 
     * these properties it tells jaxb that Woodstox has already interned the strings which causes it to disable its 
     * string interning. 
     * 
     * We did explore the cleaner option of pulling in the Maven "javax.xml:jaxb-impl" artifact as a dependency instead of using 
     * the JVM's jaxb library. That external jaxb library when used with the FastInfoset library does perform substantially better 
     * than the JVM's but isn't 100% as fast as the JVM's with interning disabled. The key reason that we quit exploring that solution 
     * is that when you repackage (via maven-shade-plugin) the jaxb libraries they no longer work with our standard jaxb binding 
     * maven components due to statements like "if (instanceof my_repackaging_project.shaded.XMLElement)" 
     * used during the data mapping process. 
     */ 
     public JaxbStringInternSuppressionStaxManager enableTrickToStopJaxbFromInterningStrings() { 
      super.setProperty("org.codehaus.stax2.internNames", true); 
      super.setProperty("org.codehaus.stax2.internNsUris", true); 
      return this; 
     } 
    } 

Lösung Option 3: Genug Leute, die einen JVM Support-Vertrag mit Oracle haben erhöhen Tickets für Nicht-interne fastinfoset Unterstützung irgendeine Art zu fragen.

Ich würde erwarten, dass es relativ einfach für Oracle ist, die von JVM bereitgestellte Jaxb-Implementierung zu lehren, um aus dem angegebenen XMLStreamReader zu ermitteln, dass diese Fastinfoset-Implementierung für interne Strings konfiguriert ist.

Lösung Möglichkeit, dass nicht geklappt hat pan: Packen Sie die beiden Gläser aus der Lösung 1 oben

Man kann „Maven-Schatten-Plug-in“ oder ähnlich erstellen neue Gläser mit benutzerdefinierten Präfix Paketnamen verwenden. Das hat mit diesen Bibliotheken nach einigem Herumspielen funktioniert. Doch das Endergebnis, dass ich kam, war, dass die neu verpackt jaxb Bibliotheken nun die jaxb-RI produzierten OXM Objekte Anmerkungen zu haben, aus den neuen schattierte Paketnamen wollten. Meine wurden standardmäßig erstellt, sodass meine neu gepackte Lösung keine Daten zu meinen Objekten zuordnen würde.Ich bin nicht bereit, zu diktieren, dass unsere OXM-Bindungsbibliotheken eine neu gepackte jaxb-Bibliothek verwenden, und ich mochte diesen Ansatz auch nicht genug, um Wege zum Umpacken genauer zu erforschen, um das für diese Anmerkungen verwendete Paket nicht zu ändern.

Lösung Option, die ich nicht entdecken: ".internal"

der fastinfoset Klassen JVM verwenden, die haben in ihren Paketnamen. Diese würden wahrscheinlich gut mit der Jaxb-Implementierung funktionieren, die mit der JVM geliefert wird, aber ich weigere mich, "zukünftige mich" den Supportkosten auszusetzen, die mit der Verwendung von interner Apis kommen.

Verwandte Themen