how to use java nio.channel with ObjectInputStream?

Discussions

Performance and scalability: how to use java nio.channel with ObjectInputStream?

  1. how to use java nio.channel with ObjectInputStream? (4 messages)

    We are trying to port a current system from JDK 1.3 to JDK 1.4 using java nio.
    I want to use java.nio.SocketChannel for asynchronous IO, order to reduce the number of threads bound to a Socket object and reading request from it in the past. The asynchronous IO is exactly what I want.

    But our system is using ObjectInputStream and ObjectOutputStream now. As a SocketChannel only support read/write using ByteBuffer. So I would like to hear others suggestion here.
  2. After literally years of experimenting, I've come to the conclusion that ObjectOutputStream and it's input sister are evil and should be eradicated from the planet.

    Barring that, and since sometimes you do need to serialize an object, what I do is create an ObjectOutputStream backed by a ByteArrayOutputStream, writeObject(), grab the buffer, then use that for your input into a ByteBuffer.

    If you want better performance, you can also very easily create something like a ByteBufferOutputStream() by extending OutputStream, with your ByteBufferOutputStream backed by a direct ByteBuffer. If you synchronize use of the BBOS properly, you can even reuse and resize the underlying ByteBuffer as necessary and avoid unnecessary buffer creations.

    You can also use:

       Channels.newInputStream (ReadableByteChannel)
       Channels.newOutputStream (WritableByteChannel)

    (there are also Streams-to-buffers static methods as well). I rolled my own to have better control, but it might be easier to use the above.

    As an FYI, be careful if you're using Selectors and pre-Java 1.4 Stream code. When you pop out of a Select your Channel is in non-blocking mode. You'll need to either temporarily set it back to blocking (and then re-non-block it when adding it back to the Selector) or go fully non-blocking, which involves holding onto your ByteBuffer, reading/writing what you can, and re-injecting back into the Selector for read/write events and being sure to re-use the original Buffer when reading/write again (until you've got the data you need).

    As a side note, since ObjectOutputStream buffers everything prior to 1.4, you have to flush it after each use, and I've found the flushing is an enormous CPU hog on the receiving end for some reason. It's actually to create a new ObjectOutputStream each time you're sending an object and then throw it away then to re-use an existing one.

        -Mike
  3. I would extend ByteArrayOutputStream to get access to the protected fields buf and count, so that you don't have to call toByteArray() (which stupidly clone the array, talk about memory waste).

    Then I would do as said before, wrap this baos with oos, and write the assembled byte[] in your bytebuffer.

    Client impl is trivial, bytearrayinputstream, although you could also extend to get access to protected fields, and exploit the "mark" & "reset" to rewind after filling the byte[] from the byte buffer.)

    All solutions involve a "bytebuffer" pump.

    Note: try externalizable instead of serializable, it requires you to code the persistence of fields, but it is much faster than default serialization which involves the native jvm code for reflection on fields. You may also find that you want your own protocol stack at some point, given that you are about to write bytes and not objects. In that perspective, serialization is all-or-nothing for me.
  4. \Quartz Quartz\
    I would extend ByteArrayOutputStream to get access to the protected fields buf and count, so that you don't have to call toByteArray() (which stupidly clone the array, talk about memory waste).

    Then I would do as said before, wrap this baos with oos, and write the assembled byte[] in your bytebuffer.

    Client impl is trivial, bytearrayinputstream, although you could also extend to get access to protected fields, and exploit the "mark" & "reset" to rewind after filling the byte[] from the byte buffer.)
    \Quartz Quartz\

    Yep, this is effectively what I do (except that I chose to subclass InputStream/OutputStream and use byte buffers under the covers). And I agree on the ByteArrayOutputStream toByteArray() call - it's silly that it clones the array every time, this wastes memory _and_ is slow.

    \Quartz Quartz\
    Note: try externalizable instead of serializable, it requires you to code the persistence of fields, but it is much faster than default serialization which involves the native jvm code for reflection on fields. You may also find that you want your own protocol stack at some point, given that you are about to write bytes and not objects. In that perspective, serialization is all-or-nothing for me.
    \Quartz Quartz\

    I think it's an excellent idea to write a little-protocol no matter what. It insulates you from a number of problems, and can make your connection survive things like ClassNotFoundExceptions. What I typically do is write a protocol somewhat like this for simple serialization:

        <magic number> [integer]
        <msg length> [integer]
        <bytes> [Serialized object]

    The <magic number> identifies the protocol, so if someone connects with the wrong kind of client you can reject it (you can also then support multiple protocols, and allow different versions of clients to connect to your server even if you need to make a protocol change). <msg_length> is the length of the following byte array, the byte array is the result of calling writeObject() on an ObjectOutputStream backed by a ByteArrayOutputStream or a custom sub-class which uses NIO buffers instead.

    Externalizable is also a good idea if you control the classes - it is lots faster.

        -Mike
  5. Non-Blocking Java Serialization[ Go to top ]

    Client side:
    Socket server = Socket(serverAddress, serverPort).connect();
    OutputStream outStream = server.getOutputStream();
    OutputObjectStream objectOutStream = new OutputObjectStream(outStream);

    //write to server
    outStream.write(1);
    outObjectStream.write(MySerializableObject);


    ServerSide:

    Socket client = serverSocket.accept();
    InputStream inStream = client.getInputStream();
    InputObjectStream inObjectStream = new InputObjectStream(inStream);

    //main driving while loop of the server
    while (running_forever) {
    //do all kinds of work
    if (inStream.available() >0) { //NON-BLOCKING
    //read in the number the client sent (pick it off the stream)
    inStream.read();
    //read in the object the client sent
    Object = inObjectStream.readObject();
    }

    //do other kinds of work

    }