[ 
https://issues.apache.org/jira/browse/DIRMINA-827?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Emmanuel Lecharny reassigned DIRMINA-827:
-----------------------------------------

    Assignee: Emmanuel Lecharny

> NioSocketConnector leaks the last open NioSocketSession after close
> -------------------------------------------------------------------
>
>                 Key: DIRMINA-827
>                 URL: https://issues.apache.org/jira/browse/DIRMINA-827
>             Project: MINA
>          Issue Type: Bug
>          Components: Core
>    Affects Versions: 2.0.2
>         Environment: Windows 7/Solaris SPARC/Solaris x86
> Java versions 6u18 & 6u24
>            Reporter: Matt Yates
>            Assignee: Emmanuel Lecharny
>            Priority: Critical
>              Labels: connector, mina, session
>         Attachments: MinaMain-2011-04-10.hprof, mina-core-2.0.4-SNAPSHOT.jar, 
> mina-core-2.0.4-SNAPSHOT.jar, sessionGcRoot.jpg
>
>
> My company's program uses MINA to make multiple (usually) simultaneous 
> connections to various other machines, while reusing the same 
> NioSocketConnector for each new connection.  For better or worse, we store 
> various objects in the IoSession's attributes, which we expect to get 
> released on close.  This is not always the case, however, as said session 
> remains in memory until either a new connection is made or the IoConnector is 
> disposed.  After writing the simplest Connector program I could (I have 
> several servers available on my network, so I did not write a matching 
> Acceptor), and performing some profiling and debugging, I was able to confirm 
> the leak and identify the issue.
> Below is my Connector test program
> {code}
> public class MinaMain {
>     private static final Logger LOGGER = 
> LoggerFactory.getLogger(MinaMain.class);
>     public static void main(String[] args) throws InterruptedException {
>         LOGGER.trace("Waiting for YourKit to start");
>         Thread.sleep(15000);
>         NioSocketConnector connector = new NioSocketConnector();
>         connector.setHandler(new IoHandlerAdapter());
>         closeSession(getConnectedSession(connector));
> //        LOGGER.info("Creating and closing 5 sessions in series");
> //
> //        for (int x = 0; x < 5; x++) {
> //            IoSession session = getConnectedSession(connector);
> //
> //            if (session == null) {
> //                continue;
> //            }
> //
> //            closeSession(session);
> //        }
> //
> //        LOGGER.info("Creating 5 sessions and then closing 5 sessions");
> //        IoSession[] sessions = new IoSession[5];
> //
> //        for (int x = 0; x < 5; x++) {
> //            sessions[x] = getConnectedSession(connector);
> //        }
> //
> //        for (int x = 0; x < 5; x++) {
> //            IoSession session = sessions[x];
> //
> //            if (session != null) {
> //                closeSession(session);
> //                sessions[x] = null;
> //            }
> //        }
>         LOGGER.info("Test complete. Sleeping for 60s");
>         Thread.sleep(60000);
>     }
>     private static IoSession getConnectedSession(IoConnector connector) 
> throws InterruptedException {
>         IoSession session = null;
>         try {
>             ConnectFuture future = connector.connect(new 
> InetSocketAddress("134.64.37.183", 11109));
>             future.addListener(new IoFutureListener<ConnectFuture>() {
>                 @Override
>                 public void operationComplete(ConnectFuture future) {
>                     LOGGER.debug("Connection completed callback for session " 
> + future.getSession().getId());
>                 }
>             });
>             future.awaitUninterruptibly();
>             LOGGER.debug("Connection created for session " + 
> future.getSession().getId());
>             session = future.getSession();
>             Thread.sleep(15000);
>         }
>         catch (Exception e) {
>             LOGGER.error("Failed to connect", e);
>         }
>         return session;
>     }
>     private static void closeSession(IoSession session) throws 
> InterruptedException {
>         try {
>             CloseFuture closeFuture = session.getCloseFuture();
>             closeFuture.addListener(new IoFutureListener<CloseFuture>() {
>                 @Override
>                 public void operationComplete(CloseFuture future) {
>                     LOGGER.debug("Session closed callback for session " + 
> future.getSession().getId());
>                 }
>             });
>             LOGGER.debug("Attempting to close session " + session.getId());
>             session.close(false);
>             LOGGER.debug("IoSession.close(false) returned. Awaiting 
> uninterruptibily for session " + session.getId());
>             closeFuture.awaitUninterruptibly();
>             LOGGER.debug("Close completed for session " + session.getId());
>         }
>         catch (Exception e) {
>             LOGGER.error("Failed to close session " + session.getId());
>             session.close(true);
>         }
>         Thread.sleep(15000);
>     }
> }
> {code}
> Attached is the hprof file of my program, taken after the program has run 
> (during the 60 second sleep at the end) and a screen shot of the GC Root of 
> the leaked NioSocketSession.
> After a couple days of debugging, I've identified why this is happening.  
> Below is a snippet from the AbstractPollingIoConnector.Connector thread
> {code}
> private class Connector implements Runnable {
>         public void run() {
>             int nHandles = 0;
>             while (selectable) {
>                 try {
>                     // the timeout for select shall be smaller of the connect
>                     // timeout or 1 second...
>                     int timeout = (int)Math.min(getConnectTimeoutMillis(), 
> 1000L);
>                     int selected = select(timeout);
>                     nHandles += registerNew();
>                     if (selected > 0) {
>                         nHandles -= processConnections(selectedHandles());
>                     }
>                     processTimedOutSessions(allHandles());
>                     nHandles -= cancelKeys();
>                     if (nHandles == 0) {
>                         synchronized (lock) {
>                             if (connectQueue.isEmpty()) {
>                                 connector = null;
>                                 break;
>                             }
>                         }
>                     }
>                 }
> ...
> {code}
> When IoSession.close(boolean) is called, the above Connector thread cancels 
> the SelectionKey associated with the session, which reduces the number of 
> handles from 1 to 0.  Immediately afterwards, the thread stops executing 
> because there are no handles and nothing in the connetQueue, however, the 
> previously cancelled key is still in the Selector because its cancelledKeys 
> Set is not processed until the next call to select(int)
> Calling dispose on the IoConnector restarts the Connector thread, which calls 
> Selector.select(int), at which time, the Selector cleans out its canclledKeys.
> Creating a new connection causes the same thing to happen, because the new 
> session also recreates the AbstractPollingIoConnector.Connector thread, as 
> there is a new handle to be processed.

--
This message is automatically generated by JIRA.
For more information on JIRA, see: http://www.atlassian.com/software/jira

Reply via email to