2009-11-21 8 views
32

Qualcuno ha mai visto un'implementazione di java.nio.ByteBuffer che crescerà dinamicamente se una chiamata putX() supera la capacità?Growing ByteBuffer

Il motivo che voglio fare in questo modo è duplice:

  1. Non so quanto spazio ho bisogno prima del tempo.
  2. Preferisco non fare un nuovo ByteBuffer.allocate() quindi un bulk put() ogni volta che esaurisco lo spazio.

risposta

25

Affinché l'I/O asincrono funzioni, è necessario disporre di memoria continua. In C è possibile tentare di riassegnare un array, ma in Java è necessario allocare nuova memoria. È possibile scrivere su un ByteArrayOutputStream e convertirlo in un ByteBuffer nel momento in cui si è pronti a inviarlo. Il rovescio della medaglia è che si sta copiando la memoria, e una delle chiavi per l'IO efficiente sta riducendo il numero di volte in cui la memoria viene copiata.

+1

ByteArrayOutputStream è in realtà esattamente ciò che voglio (in realtà non sto facendo alcun I/O, ho solo qualche serializzazione complessa da fare). Grazie! – Seth

+1

Seth, la tua affermazione della domanda ("putX") implicava che avresti bisogno di metodi come putInt, putDouble, ecc., Il che implicava che ByteArrayOutputStream non sarebbe stato sufficiente per te, quindi la mia risposta di ByteArrayDataOutput. –

+0

Non ti chiederò nemmeno come il buf di dimensioni fisse sia esattamente quello che vuoi dopo aver chiesto un buf di dimensioni illimitate. Viviamo nel mondo in cui l'assurdo è una norma. – Val

6

Dai un'occhiata alla Mina IOBuffer https://mina.apache.org/mina-project/userguide/ch8-iobuffer/ch8-iobuffer.html che è un rimpiazzo (che avvolge il ByteBuffer)

Tuttavia, vi consiglio di allocare più del necessario e non preoccuparti troppo. Se si assegna un buffer (specialmente un buffer diretto), il sistema operativo gli fornisce memoria virtuale ma utilizza solo memoria fisica quando è effettivamente utilizzata. La memoria virtuale dovrebbe essere molto economica.

+6

Adoro l'avviso nella pagina: "Il motivo principale per cui MINA ha il proprio wrapper su nio ByteBuffer è quello di avere buffer estensibili. Questa è stata una decisione pessima." – Suppressingfire

+0

In effetti, scrivere in memoria implica che alla fine vuoi un limite e, inoltre, non un limite così grande. È ancora curioso sapere se la parte inutilizzata di ArrayBuffer rimane libera per altre applicazioni/usi o meno. – Val

+1

Link è morto. 404. – luckydonald

8

Un ByteBuffer non può davvero funzionare in questo modo, poiché il suo concetto di design deve essere solo una vista di uno specifico array, a cui si può anche fare riferimento diretto. Non poteva provare a scambiare quell'array con un array più grande senza che si verifichino stranezze.

Quello che si desidera utilizzare è un DataOutput. Il modo più conveniente è quello di utilizzare la libreria Guava (pre-release):

ByteArrayDataOutput out = ByteStreams.newDataOutput(); 
out.write(someBytes); 
out.writeInt(someInt); 
// ... 
return out.toByteArray(); 

ma si potrebbe anche creare un DataOutputStream da un ByteArrayOutputStream manualmente, e solo affrontare le IOExceptions spuri da loro concatenamento in AssertionErrors.

+0

Né "ByteArrayDataOutput' né' ByteStreams' vengono visualizzati nel JDK, come di Java 8. A cosa ti riferisci? – EJP

+0

@EJP Queste sono classi di [Google Guava] (https://code.google.com/p/guava-libraries/) come menzionato da Kevin. – Jesper

4

Potrebbe anche valere la pena dare un'occhiata a Netty's DynamicChannelBuffer. Le cose che trovo a portata di mano sono:

  • slice(int index, int length)
  • operazioni senza segno
  • separati Scrittore e lettore indici
4

Un'altra opzione è quella di utilizzare la memoria diretta con un buffer di grandi dimensioni. Questo consuma memoria virtuale ma utilizza solo la quantità di memoria fisica utilizzata (per pagina che è in genere 4K)

Quindi se si assegna un buffer di 1 MB, si ottiene 1 MB di memoria virtuale, ma l'unico SO dà fisico pagine per l'applicazione che viene effettivamente utilizzata.

L'effetto è che si vede l'applicazione utilizzando un sacco di memoria virtuale, ma una quantità relativamente piccola di memoria residente.

+0

aggiungere qualche esempio di codice – Alex

-2

Un vettore consente una crescita continua

Vector<Byte> bFOO = new Vector<Byte>(); bFOO.add ((byte) 0x00);

+5

Con questo metodo, per ogni byte è necessario creare un oggetto Byte che avrà un'intestazione di 8 byte, +1 byte per memorizzare il valore all'interno dell'oggetto. Ora, tutti gli oggetti java occupano più 8 byte, quindi fanno 16 byte per oggetto. Diciamo che stiamo usando un sistema a 32 bit, quindi i riferimenti a questi oggetti nel vettore sono 4 byte ciascuno. Quindi per memorizzare ogni byte sono necessari 20 byte di memoria. Questo non è molto buono. – Numeron

+0

@Numeron Byte è un peso mosca, ci sono esattamente 256 istanze nella JVM a meno che non si chiami 'new' invece di 'valueOf'. Auto-boxing fa quest'ultimo. Ma la risposta è scarsa in entrambi i casi, in quanto l'indicizzazione in box sarà significativamente più grande e più lenta anche se non ci sono oggetti Byte allocati. –

-6

Per serializzare qualcosa è necessario inserire l'oggetto. Quello che puoi fare è mettere il tuo oggetto in una collezione di oggetti, e dopo fare quel ciclo per ottenere l'iteratore e metterli in una matrice di byte. Quindi, chiama ByteBuffer.allocate(byte[].length). Questo è quello che ho fatto e ha funzionato per me.

Problemi correlati