2015-01-21 5 views
6

Ich bin auf der Suche nach einer Möglichkeit, String aus einem byte[] in Java mit so wenig Müll wie möglich zu deserialisieren. Da ich meinen eigenen Serialisierer und Deserialisierer erstelle, habe ich die völlige Freiheit, irgendeine Lösung auf der Serverseite (d. H. Beim Serialisieren von Daten) und auf der Clientseite (d. H. Beim Deinserialisieren von Daten) zu implementieren.Zero-Müll große String-Deserialisierung in Java, Humongous Objekt Ausgabe

I haben, um effizient Serialisieren ein String ohne anfallenden Müll jede Overhead durch Iterieren durch die String's Zeichen verwaltet (String.charAt(i)) und Umwandeln jedes char (16-Bit-Wert) 8-Bit-Wert 2x. Es gibt eine nette Debatte über diese here. Eine Alternative ist die Verwendung von Reflection zum direkten Zugriff auf String'schar[], aber dies liegt außerhalb des Bereichs des Problems.

Allerdings scheint es mir unmöglich, die byte[] deserialisieren, ohne die char[]zweimal zu schaffen, die, na ja, seltsam scheint.

Das Verfahren:

  1. char[] erstellen
  2. Iterate durch byte[] und füllen Sie das char[]
  3. String erstellen mit String(char[]) Konstruktor

Wegen String Unveränderlichkeit Regeln Java, der Konstruktor Kopiert den char [] und erstellt 2x GC Overhead. Ich kann immer Mechanismen verwenden, um dies zu umgehen (unsichere String Zuweisung + Reflexion, um die char[] Instanz zu setzen), aber ich wollte nur fragen, ob es irgendwelche Auswirkungen auf diese andere als ich brechen jede Konvention auf String's Unveränderlichkeit.

Natürlich wäre die weiseste Antwort darauf "Komm schon, hör auf damit und vertraue auf GC, das Original char[] wird extrem kurzlebig und G1 wird es momentan loswerden", was eigentlich Sinn macht , , wenn die char[] kleiner ist als 1/2 der G1-Regionsgröße. Wenn es größer ist, wird char [] direkt als ein menschliches Objekt zugewiesen (d. H. Automatisch außerhalb der Region von G1 verbreitet). Solche Objekte sind extrem schwer effizient in G1 zu sammeln. Deshalb ist jede Zuweisung wichtig.

Irgendwelche Ideen, wie man das Problem angeht?

Vielen Dank.

+0

Haben Sie in Betracht gezogen, einfach nicht mit Strings zu arbeiten und nur die Rohbyte-Daten zu serialisieren und Zeichensatzkonvertierungen an Teilabschnitten durchzuführen, wenn es absolut notwendig ist? – the8472

+0

Ich habe. Meine Idee war es, eine neue Klasse 'MutableString' zu erstellen, und eine Menge von traditionell mülllastigen Operationen über ihr zu implementieren (zum Beispiel Fastpath 'String' Split) und dann eine Methode' toString (from, to) 'zu erstellen eine "view" -Instanz, die vom Typ "String" ist. Das könnte ich machen. Dies würde jedoch erfordern, unsere Anwendung komplett zu überarbeiten und "MutableString" überall zu verwenden. Es ist eine nette Idee, aber ich wollte zuerst Alternativen erkunden. – SergioTCG

+1

Wissen Sie, dass all diese Dinge bereits existieren? Es gibt 'CharBuffer' und' StringBuilder', beide sind eine Art veränderbarer 'String' (es sei denn, Sie haben eine unveränderliche Ansicht erstellt), es gibt Methoden zum Erstellen leichter Untersequenzen von ihnen und sie implementieren alle' CharSequence', die 'Schnittstelle ', auf dem das Regex-Paket, das tatsächlich die' Split'-Operation implementiert, arbeitet. Und während * aussieht * wird der Zeicheninhalt immer kopiert, wenn zwischen 'String's,' CharBuffer's und 'StringBuilder's beim Betrachten des Quellcodes umgeschaltet wird. HotSpot hat spezielle Optimierungen für sie ... – Holger

Antwort

1

Ich habe eine Lösung gefunden, die nutzlos ist, wenn Sie eine nicht verwaltete Umgebung haben.

Die Klasse java.lang.String hat einen package-private Konstruktor String(char[] value, boolean share).

Quelle:

/* 
* Package private constructor which shares value array for speed. 
* this constructor is always expected to be called with share==true. 
* a separate constructor is needed because we already have a public 
* String(char[]) constructor that makes a copy of the given char[]. 
*/ 
String(char[] value, boolean share) { 
    // assert share : "unshared not supported"; 
    this.value = value; 
} 

Diese ausgiebig in Java verwendet werden, z.B. in Integer.toString(), Long.toString(), String.concat(String), String.replace(char, char), String.valueOf(char).

Die Lösung (oder Hack, was auch immer Sie es nennen wollen) ist, die Klasse zu java.lang Paket zu verschieben und auf den Paket-privaten Konstruktor zuzugreifen. Das wird nicht gut mit dem Sicherheitsmanager verheißen, aber das kann umgangen werden.

+5

Anstatt eine Klasse in das Paket zu verschieben, könnten Sie wahrscheinlich einfach per Reflektion auf das Konstrukt zugreifen und dann eine Methode handle/lambda für die Konstruktormethode erstellen, um den Aufruf von Gemeinkosten zu vermeiden – the8472

3

Solche Objekte sind extrem schwierig, in G1 effizient Müll gesammelt werden.

Dies ist möglicherweise nicht mehr wahr, aber Sie müssen es für Ihre eigene Anwendung auswerten. JDK Bugs 8027959 und 8048179 führen neue Mechanismen zum Sammeln humongöser, kurzlebiger Objekte ein. Entsprechend den Fehlerflags müssen Sie möglicherweise mit den jdk-Versionen ≥8u40 und ≥8u60 laufen, um ihre jeweiligen Vorteile zu erzielen.

Experimentelle Option von Interesse:

-XX:+G1ReclaimDeadHumongousObjectsAtYoungGC 

Tracing:

-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC 

Für weitere Ratschläge und Fragen dieser Merkmale in Bezug auf die ich hotspot-gc-use Mailing-Liste schlagen würde empfehlen.

+0

Danke, ich werde Schau mal. – SergioTCG