Hi all,

I wrote a relatively simple Mina app that acts as a logging "man in the 
middle".  It's run fine on a production server for over a year (last git 
commit, 2012-12-04).

Yesterday, I put a new production server in place running Ubuntu 12.04 LTS.

pfixfilt@mail:~$ java -version
java version "1.6.0_30"
OpenJDK Runtime Environment (IcedTea6 1.13.1) (6b30-1.13.1-1ubuntu2~0.12.04.1)
OpenJDK 64-Bit Server VM (build 23.25-b01, mixed mode)

I am using mina-core-2.0.4, and groovy 2.0.8.

Depending on the load, the program runs out of heap on the new server, fairly 
consistently.  It is still running on another server that is also running 
Ubuntu 12.04 LTS and the same java version.  The code there has never run out 
of memory, to my knowledge.

I managed to get a dump from the process the last time it ran out of memory.  
My old version of jprofiler shows that most of the memory is taken up by char[] 
(6,355kb), the next largest is java.lang.Class (897kb).

I'm assuming the problem is my mina code, but I can't figure out how it's 
running fine on one server but not another.

My code works by extending IoHandlerAdapter. The messageRecieved method is 
simple enough

    @Override
    void messageReceived(IoSession session, Object message) {
        IoBuffer rb = (IoBuffer) message;
        IoBuffer wb = IoBuffer.allocate(rb.remaining())
        rb.mark()
        wb.put(rb)
        wb.flip()
        ((IoSession) session.getAttribute(OTHER_IO_SESSION)).write(wb)
        rb.reset()

        FileOutputStream outputStream = (FileOutputStream) 
session.getAttribute(OUTPUT_STREAM)
        if (outputStream != null) {
            outputStream.write(wb.array())
        }
     }

Looking at this code, I allocate and IoBuffer that I never explicitly free, but 
I don't think I can/should because of the async nature of the code.

The "inbound handler" is simply listening for connections, and when one is 
received it opens an outbound connection and saves references to the "other" 
connection in each session, to form a pair.

class InboundHandler extends AbstractProxyIoHandler {

    private final OutboundHandler connectorHandler = new OutboundHandler()
    private static final File TMP_DIR = new 
File(System.getProperty('user.home') + '/tmp')

    static {
        if (!TMP_DIR.exists()) {
            TMP_DIR.mkdirs()
        }
    }

    NioSocketConnector connector

    InboundHandler(NioSocketConnector connector) {
        this.connector = connector
        connector.setHandler(connectorHandler)
    }

    @Override
    void sessionCreated(IoSession session) {

        log.debug("enter sessionCreated")

        InetSocketAddress remoteAddress = new 
InetSocketAddress(Config.instance.data.send.host, 
Config.instance.data.send.port)

        connector.connect(remoteAddress).addListener(new 
IoFutureListener<ConnectFuture>() {

            @Override
            void operationComplete(ConnectFuture future) {
                try {
                    File outputFile = File.createTempFile("postfix-filter", 
".txt", TMP_DIR)
                    outputFile.setReadable(true, true)
                    outputFile.setWritable(true, true)
                    log.debug("Created file: " + outputFile.absolutePath)
                    session.setAttribute(OUTPUT_FILE, outputFile)
                    session.setAttribute(OUTPUT_STREAM,new 
FileOutputStream(outputFile))

                    future.session.setAttribute(OTHER_IO_SESSION, session)
                    session.setAttribute(OTHER_IO_SESSION, future.session)
                    IoSession session2 = future.session
                    session2.resumeRead()
                    session2.resumeWrite()

                }
                catch (RuntimeIoException e) {
                    session.close(true)
                }
                catch (IOException ioe) {
                    log.error("Unable to create tempfile: " + ioe.message, ioe)
                    session.close(true)
                }
                finally {
                    session.resumeRead()
                    session.resumeWrite()
                }
            }
        })
    }
}

When sessions are closed, I close the other end and cleanup the rest of the 
resources

    @Override
    void sessionClosed(IoSession session) {
        log.debug("Enter session closed: " + session.id)
        if (session.getAttribute(OTHER_IO_SESSION) != null) {
            IoSession otherSession = (IoSession) 
session.getAttribute(OTHER_IO_SESSION)
            log.debug("Closing session, otherSession: " + otherSession.id)
            otherSession.setAttribute(OTHER_IO_SESSION, null)
            session.close(false)
            session.setAttribute(OTHER_IO_SESSION, null)
        }

        FileOutputStream outputStream = (FileOutputStream) 
session.getAttribute(OUTPUT_STREAM)
        if (outputStream != null) {
            outputStream.close()

            File file = (File) session.getAttribute(OUTPUT_FILE)

            if (file != null && file.exists()) {
                MessageData md = new MessageData()
                file.eachLine { line -> md.processLine(line) }
            }

            file.delete()
        }
    }


Does anyone see anything obvious that I missed?  I may try upgrading my mina 
version to see if that helps, but unless I can find a glaring mistake I'd 
rather not change the code too much.

Thank you all very much for your time, and any help you can offer.

Tony Nelson
Starpoint Solutions


Since 1982, Starpoint Solutions has been a trusted source of human capital and 
solutions. We are committed to our clients, employees, environment, community 
and social concerns.  We foster an inclusive culture based on trust, respect, 
honesty and solid performance. Learn more about Starpoint and our social 
responsibility at http://www.starpoint.com/social_responsibility

This email message from Starpoint Solutions LLC is for the sole use of  the 
intended recipient(s) and may contain confidential and privileged  information. 
 Any unauthorized review, use, disclosure or distribution is prohibited.  If 
you are not the intended recipient, please contact the sender by reply email 
and destroy all copies of the original message.  Opinions, conclusions and 
other information in this message that do not relate to the official business 
of Starpoint Solutions shall be understood as neither given nor endorsed by it.

Reply via email to