2015-01-21 7 views
6

Sto cercando un modo per deserializzare uno String da un byte[] in Java con il minor numero di rifiuti possibile. Poiché sto creando il mio serializzatore e il de-serializzatore, ho la completa libertà di implementare qualsiasi soluzione sul lato server (ad esempio durante la serializzazione dei dati) e sul lato client (ad esempio, quando la serializzazione dei dati).Deserializzazione Zero-Garbage String in Java, problema oggetto Humongous

sono riuscito efficientemente serializzare un String senza incorrere in alcun overhead immondizia scorrendo le String's caratteri (String.charAt(i)) e convertendo ogni char (valore a 16-bit) a 2x valore a 8 bit. C'è un bel dibattito su questo here. Un'alternativa è utilizzare Reflection per accedere direttamente allo char[], ma questo non rientra nell'ambito del problema.

Tuttavia, sembra impossibile per me deserializzare il byte[] senza creare il char[]due volte, che sembra, beh, strano.

La procedura:

  1. Creare char[]
  2. Scorrere byte[] e compilare il char[]
  3. Crea String con String(char[]) costruttore

A causa di String regole immutabilità di Java, il costruttore copia il carattere [], creando 2x overhead GC. Posso sempre usare i meccanismi per eludere questo (allocazione non sicura String + Reflection per impostare l'istanza char[]), ma volevo solo chiedere se ci sono delle conseguenze su questo diverso da me che infrange ogni convenzione sull'immutabilità String's.

Naturalmente, la risposta più saggia a questo sarebbe "dai, smetti di fare questo e abbi fiducia in GC, l'originale char[] sarà estremamente breve e G1 se ne libererà momentaneamente", che in realtà ha senso , se lo char[] è inferiore a 1/2 della dimensione della regione del G1. Se è più grande, il carattere [] verrà assegnato direttamente come oggetto gigantesco (cioè propagato automaticamente al di fuori della regione del G1). Tali oggetti sono estremamente difficili da raccogliere in modo efficiente in G1. Ecco perché ogni assegnazione è importante.

Qualche idea su come affrontare il problema?

Molte grazie.

+0

hai semplicemente considerato di non lavorare con le stringhe e solo serializzare i dati di byte grezzi e fare conversioni di set di caratteri nelle sottosezioni quando è assolutamente necessario? – the8472

+0

Ho. La mia idea era quella di creare una nuova classe 'MutableString', e implementare molte delle tradizionali operazioni spazzatura su di essa (per esempio, la divisione' String' di fastpath), e poi avere un metodo 'toString (from, to)' che crea un'istanza "view" che è di tipo 'String'. Potrei farlo. Ma ciò richiederebbe il refactoring completo della nostra applicazione e l'uso di 'MutableString's ovunque possibile. È una buona idea, ma ho voluto esplorare prima le alternative. – SergioTCG

+1

Sei consapevole che tutte queste cose esistono già? Ci sono 'CharBuffer' e' StringBuilder', essendo entrambi una specie di 'String' mutabile (a meno che tu non abbia creato una vista immutabile), ci sono metodi per creare sottoserie leggere di essi e tutti implementano' CharSequence', l'interfaccia ' 'su cui funziona il pacchetto regex, che implementa effettivamente l'operazione' split'. E mentre sembra * il contenuto dei caratteri viene copiato tutto il tempo durante la conversione tra 'String's,' CharBuffer's e 'StringBuilder's quando si guarda il codice sorgente, HotSpot ha delle ottimizzazioni speciali per loro ... – Holger

risposta

1

Ho trovato una soluzione, che è inutile, se si dispone di un ambiente non gestito.

La classe ha un costruttore pacchetto-privato String(char[] value, boolean share).

Fonte:

/* 
* 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; 
} 

Questo è stato ampiamente utilizzato all'interno di Java, per esempio in Integer.toString(), Long.toString(), String.concat(String), String.replace(char, char), String.valueOf(char).

La soluzione (o hack, qualsiasi cosa si voglia chiamare) è spostare la classe nel pacchetto java.lang e accedere al costruttore pacchetto-privato. Questo non sarà di buon auspicio per il responsabile della sicurezza, ma questo può essere aggirato.

+5

invece di spostare una classe nel pacchetto si potrebbe probabilmente accedere al costrutto tramite reflection e quindi creare un metodo handle/lambda per il metodo del costruttore per evitare di chiamare i sovraccarichi – the8472

3

Tali oggetti sono estremamente difficili da raccogliere in modo efficiente in G1.

Questo potrebbe non essere più vero, ma sarà necessario valutarlo per la propria applicazione. Gli errori JDK 8027959 e 8048179 introducono nuovi meccanismi per la raccolta di oggetti voluminosi e di breve durata. In base alle flag di errore potrebbe essere necessario eseguire le versioni jdk ≥8u40 e ≥8u60 per sfruttare i rispettivi vantaggi.

opzione Sperimentale di interesse:

-XX:+G1ReclaimDeadHumongousObjectsAtYoungGC 

Tracing:

-XX:+G1TraceReclaimDeadHumongousObjectsAtYoungGC 

Per ulteriori consigli e le domande per quanto riguarda quelle caratteristiche mi sento di raccomandare che colpisce il hotspot-gc-use mailing list.

+0

Grazie, lo farò guarda. – SergioTCG