2009-11-21 7 views
32

¿Alguien ha visto alguna vez una implementación de java.nio.ByteBuffer que crecerá dinámicamente si una llamada putX() sobrepasa la capacidad?Crecimiento de ByteBuffer

La razón por la que quiero hacerlo de esta manera es doble:

  1. No sé la cantidad de espacio que necesito antes de tiempo.
  2. Prefiero no hacer un nuevo ByteBuffer.allocate() luego un bulk put() cada vez que me quedo sin espacio.

Respuesta

25

Para que la E/S asíncrona funcione, debe tener memoria continua. En C puede intentar volver a asignar una matriz, pero en Java debe asignar nueva memoria. Puede escribir a ByteArrayOutputStream, y luego convertirlo a ByteBuffer en el momento en que esté listo para enviarlo. La desventaja es que está copiando memoria, y una de las claves para una E/S eficiente es reducir el número de veces que se copia la memoria.

+1

ByteArrayOutputStream es en realidad exactamente lo que quiero (en realidad no estoy haciendo ninguna E/S, solo tengo que hacer una serialización compleja). ¡Gracias! – Seth

+1

Seth, su declaración de la pregunta ("putX") implicaba que necesitaría métodos como putInt, putDouble, etc., lo que implicaba que ByteArrayOutputStream no sería suficiente para usted, de ahí mi respuesta de ByteArrayDataOutput. –

+0

Ni siquiera preguntaré cómo el buf de tamaño fijo es exactamente lo que quieres después de pedir un buf de tamaño ilimitado. Estamos viviendo en el mundo donde lo absurdo es una norma. – Val

6

Tenga una mirada en Mina IOBuffer https://mina.apache.org/mina-project/userguide/ch8-iobuffer/ch8-iobuffer.html que es una gota en el reemplazo (que envuelve el ByteBuffer)

Sin embargo, sugiero que asignar más de lo necesario y no se preocupe por ello demasiado. Si asigna un búfer (especialmente un búfer directo), el sistema operativo le otorga memoria virtual, pero solo utiliza memoria física cuando se usa realmente. La memoria virtual debe ser muy barata.

+6

Me encanta la advertencia en la página: "La razón principal por la que MINA tiene su propio contenedor en la parte superior de nio ByteBuffer es tener búferes extensibles. Esta fue una decisión muy mala". – Suppressingfire

+0

De hecho, escribir en la memoria implica que, en última instancia, desea un límite y, además, no ese límite grande. Todavía es curioso saber si la parte no utilizada de ArrayBuffer se mantiene libre para otras aplicaciones/usos o no. – Val

+1

El enlace está muerto. 404. – luckydonald

8

Un ByteBuffer realmente no puede funcionar de esta manera, ya que su concepto de diseño es solo una vista de una matriz específica, a la que también puede tener una referencia directa. No podría tratar de cambiar esa matriz por una matriz más grande sin que suceda lo extraño.

Lo que quiere usar es un DataOutput. La manera más conveniente es utilizar el (pre-lanzamiento) biblioteca de guayaba:

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

Pero también se podría crear un DataOutputStream de un ByteArrayOutputStream manualmente, y justo frente a las IOExceptions espurios encadenándolos en AssertionErrors.

+0

Ni 'ByteArrayDataOutput' ni' ByteStreams' aparecen en el JDK, a partir de Java 8. ¿A qué se refiere? – EJP

+0

@EJP Esas son las clases de [Google Guava] (https://code.google.com/p/guava-libraries/) como Kevin mencionó. – Jesper

4

También puede valer la pena echar un vistazo a Netty's DynamicChannelBuffer. Cosas que encuentro a mano son:

  • slice(int index, int length)
  • operaciones sin firmar
  • separados de escritura y lectura índices
4

Otra opción es utilizar la memoria directa con un buffer de gran tamaño. Esto consume memoria virtual pero solo usa tanta memoria física como la que usa (por página, que generalmente es 4K)

Así que si asigna un buffer de 1 MB, consume 1 MB de memoria virtual, pero el único sistema operativo proporciona páginas a la aplicación que realmente usa.

El efecto es que ve su aplicación usando mucha memoria virtual pero una cantidad relativamente pequeña de memoria residente.

+0

agregue un ejemplo de código – Alex

-2

un vector permite el crecimiento continuo

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

+5

Con este método, para cada byte necesita crear un objeto Byte que tendrá un encabezado de 8 bytes, +1 byte para almacenar el valor dentro del objeto. Ahora, todos los objetos java ocupan un múltiplo de 8 bytes, por lo que se obtienen 16 bytes por objeto. Digamos que estamos usando un sistema de 32 bits, por lo que las referencias a estos objetos en el vector son de 4 bytes cada una. Entonces, para almacenar cada byte necesita 20 bytes de memoria. Eso no es muy bueno. – Numeron

+0

@Numeron Byte es un peso mosca, hay exactamente 256 instancias en la JVM a menos que llame 'nuevo' en lugar de 'valorDef'. El auto-boxing hace esto último. Pero la respuesta es mala de cualquier forma, ya que la indirección encuadrada será significativamente más grande y más lenta, incluso si no hay objetos Byte asignados. –

-6

Para serializar algo, necesitará el objeto en la entrada. Lo que puede hacer es colocar su objeto en la colección de objetos, y luego hacer bucle para obtener el iterador y ponerlos en la matriz de bytes. Luego, llama al ByteBuffer.allocate(byte[].length). Eso es lo que hice y funcionó para mí.

Cuestiones relacionadas