Search code examples
javanio

reuse ByteBuffers across multiple ByteBufferBackedInputStreams


i have a question about using reusing ByteBuffers from java.nio multiple times to produce InputStreams. i've found a nice converter in jackson called ByteBufferBackedInputStream. my problem however is that i want to potentially have multiple InputStreams open at the same time, backed by the same underlying ByteBuffer. consider,

    @Test
    void testStuff() throws Exception {
        ByteBuffer bb = ByteBuffer.allocateDirect(42);
        for (int i = 0; i < 42; i++) {
            bb.put((byte) i);
        }
        bb.flip();
        InputStream foo = new ByteBufferBackedInputStream(bb);
        InputStream bar = new ByteBufferBackedInputStream(bb);

        byte[] eightyFourBytes = new byte[84];
        int readFromFoo = foo.read(eightyFourBytes);
        int readFromBar = bar.read(eightyFourBytes);

        assertThat(readFromFoo).isEqualTo(42);
        assertThat(readFromBar).isEqualTo(42); // oops! reading mutated the underlying ByteBuffer's position
    }

i'm scratching my head to work out a solution to this. i need this because,

  • i am using and pooling direct ByteBuffers for better file io performance (that using it is fairly drop-in for file io is important as well), and to reduce GC pressure,
  • i am in other places calling into an interface which takes an InputStream, and is inflexible,
  • my application has had issues with memory usage so i don't want to recopy the buffer

i guess my question is, is there an alternative to ByteBuffer, or a better way of using it than i am? obviously i could just manually reset the buffer between usages, but doing that seems brittle and in my mind the ideal solution can produce InputStreams that i can hand off without thinking about it.


Solution

  • The simple approach is to hand out a read only copy:

    InputStream foo = new ByteBufferBackedInputStream(bb.asReadOnlyBuffer());
    

    The buffer will be shared, but each copy will get its own position, limit and mark. Note that changes to the data in the buffer will be seen by all clients, so you need to be really sure that nobody is using your buffer before you return it to the pool again! Depending on your use case, that may be easy or very hard. If it is hard, pooling might not be such a good idea.