This project is read-only.
1
Vote

SSLStream disposed but not collected

description

Hello,

I was testing SuperSocket with a custom protocol that uses TerminatorReceiveFilterFactory and realize that using no security the process run smoothly, but using SSL the process memory (private bytes) keeps growing.

After some research and the help of a memory profiler I discover that there are many SSlStream objects disposed but not collected. So, I change the code from AsyncStreamSocketSession.cs to override the OnClosed method:
        protected override void OnClosed(CloseReason reason)
        {

            if (m_Stream != null)
            {
                m_Stream.Close();
                m_Stream.Dispose();
                m_Stream = null;
            }

            base.OnClosed(reason);

        }
After this change the problem continues and memory keeps growing, and this time I realize that the NetworkStream is not collected, even with the leaveInnerStreamOpen set to false on the SSLStream creation. So, I change the CreateSSLStream method to allow later dispose:
private Stream n_Stream;

        private SslStream CreateSslStream(ICertificateConfig certConfig)
        {
            n_Stream = new NetworkStream(Client);

            //Enable client certificate function only if ClientCertificateRequired is true in the configuration
            if(!certConfig.ClientCertificateRequired)
                return new SslStream(n_Stream, false);

            //Subscribe the client validation callback
            return new SslStream(n_Stream, false, ValidateClientCertificate);
        }
and add the dispose on the OnClosed method:

        protected override void OnClosed(CloseReason reason)
        {

            if (n_Stream != null)
            {
                n_Stream.Close();
                n_Stream.Dispose();
                n_Stream = null;
            }

            if (m_Stream != null)
            {
                m_Stream.Close();
                m_Stream.Dispose();
                m_Stream = null;
            }

            base.OnClosed(reason);

        }
Now everything appears to work and the memory is collected as well.
Can anyone confirm this behavior? Is this a correct solution?

Thanks,

comments

kerryjiang wrote Jan 8, 2016 at 1:53 AM

Let me read the code of SslStream at first

kerryjiang wrote Jan 8, 2016 at 9:01 AM

Did you debug the process memory? Could you check which classes' instances hold the reference of the network stream?

MidSilence wrote Jan 10, 2016 at 6:03 PM

Hello Mr Jiang,

The memory analyzer shows that there're too many AsyncStreamSocketSessions holding the SslStream. After a review I realize that my AppServer based server was not calling base.OnSessionClosed when I override the method, keeping the AsyncStreamSocketSession objects on the session's list. So, I undo all the changes I made on AsyncStreamSocketSession, call the base method, the session is released and everything is fine now.

I also check the SslStream code and discover that the InnerStream is only checked for Close or Flush if the Dispose is calling on code (from AuthenticatedStream Dispose). If GC called it, the InnerStream is not checked and will also collected on GC process. Although the results are the same, could you think it's a good idea on call the SslStream's Dispose method on session close?

Thanks for your attention,

Khyalis wrote Jan 11, 2016 at 9:53 AM

If you're talking about disposing the InnerStream during finalization I would advise against it.
From what I understand, the InnerStream, should it need to be finalized as well, will be queued for finalization separately and finalized independently.

kerryjiang wrote Jan 11, 2016 at 11:41 AM

I checked the source code of SslStream too. And the innerStream has the same life cycle with SslStream.

So I guess the socket session was referenced by something.

Hello @MidSilence, do you mean you overrrided the OnSessionClosed method of AppSession class but didn't invoke the methed base.OnSessionClosed();

MidSilence wrote Jan 12, 2016 at 2:49 PM

I remove the base.OnSessionClosed(session, reason) on my server, which is inherited from AppServer.
    public class MyServer : AppServer<MySession, StringRequestInfo>
    {

        protected override void OnSessionClosed(SgaServerSession session, CloseReason reason)
        {

            LogInfo("<<< Disconnected (" + reason.ToString() + ")", session.SocketId);
            base.OnSessionClosed(session, reason);   // Removed this line

        }

    }