Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Not using garbage collection

Minimize heap thrashing in your Java programs

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 2 of 4

The solution seemed pretty obvious. If you'll recall, in my July column I showed how two applets could communicate using data channels. What I needed was a way for HelloWorld to communicate to a virtual terminal. The answer was to make it out of two applets, then apply the previously workable solution: data channels.

A data channel is an interthread communication object that allows one thread to write a series of objects to another object. I was playing around with some virtual terminal applets on the Web (see the Resources section below) and decided what I really needed was an I/O stream based on the DataChannel class. Note that I could have used PipedInputStream and PipedOutputStream, but these are only one-to-one connections. With a data channel I could connect several applications to the same terminal.

First efforts

The neat thing about object-oriented languages is that you can define an interface, implement it quickly (but stupidly) to check your ideas, and then optimize the implementation for the desired performance goals. Java I/O streams are conceptually very simple things, and to implement one you need only subclass InputStream or OutputStream and then implement a couple of very simple methods. The first implementation of DataChannelOutputStream was as follows:

package util.comm;
import java.io.OutputStream;
import java.io.IOException;
public class DataChannelOutputStream extends OutputStream {
    private DataChannel dc;
    public DataChannelOutputStream(String chanID) {
        dc = DataChannel.getChannel(chanID);
    }
    public void write(int c) throws IOException {
        dc.putValue(new Integer(c));
    }
}


As you can see, 12 lines of code is pretty simple. The constructor takes the name of the underlying DataChannel, and the write() method simply spews integers (actually, they are bytes) into the channel. The other side, DataChannelInputStream, is a bit more complicated and shows why things are not as simple as one might hope. I'll dissect it in sections.

package util.comm;
import java.io.InputStream;
import java.io.IOException;
public class DataChannelInputStream extends InputStream 
                    implements Runnable {
    private Thread monitor;
    private DataChannel dc;
    private byte buffer[] = new byte[1024];
    int getIndex = 0;
    int putIndex = 0;
    public DataChannelInputStream(String chanID) {
        monitor = new Thread(this);
        dc = DataChannel.getChannel(chanID, monitor, 1024);
        monitor.start();
    }


In the first part of the class, one thing that immediately stands out is that the input stream has to start a separate thread to monitor the data channel. The basic idea is that this monitor thread will watch the channel, pull off data as it comes across, and store it in the array named buffer. The other interesting bit is that in the previous uses of DataChannels the queue was quite small, but in this class we make it much larger. This was an early attempt at overflow management.

    private boolean overflow = false;
    private synchronized void add(int b) {
        buffer[putIndex] = (byte) b;
        putIndex = (putIndex + 1) & 1023;
        if (putIndex == getIndex)
            overflow = true;
        notify();
    }
    public synchronized int read() throws IOException {
        int r;
        while (getIndex == putIndex)
            try { wait(); } catch (InterruptedException e) { }
        r = buffer[getIndex];
        getIndex = (getIndex + 1) & 1023;
        if (overflow)
            throw new IOException("Buffer overflowed - Data Lost!");
        return r;
    }


In the above section we provide the required read method of all extenders of class OutputStream that are not abstract, and we provide a method add that the monitor thread will use to store data in our holding buffer. Both are synchronized so that the read method can block (using the wait() method) when there is no data to read. The buffer is simply a circular ring of 1024 bytes that gets filled as data comes in, and the add method checks to see if data is overwriting data that hasn't been read. If it is, the flag overflow is set and an IOException is thrown when the buffer overflows so that the client can know that data was lost. More on this a bit later.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources
  • WebTerm - An applet that emulates a VT100 and connects to hosts via a telnet client.
    http://www.nacse.org/web/webterm/
  • Java telnet applet - An applet that emulates a VT320 terminal. This applet is pretty cool since it separates the terminal capability and the character display capability into two pieces.
    http://www.first.gmd.de/persons/leo/java/Telnet/
  • Another telnet applet - This is similar to the other two. Source is available.
    http://w3.gwis.com/~thorn/telnet/
  • Sources - This page contains pointers to sources from this column (and others) as well as an example of the virtual terminal I built using these classes.
    http://www.mcmanis.com/~cmcmanis/java/javaworld/