Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leak of the Socket's extension method 'ReceiveAsync'!

I have a long-time running service program based on asynchronous Socket. It's memory usage keep increasing over time when it is being health detected (short time TCP port connect and disconnect) by other devices. I suspect some memory of this type of TCP connection has not been released. So I made a simple program for verification:

class Program
{
    static void Main(string[] args) => new Program().Start(args[0] == "server");

    private void Start(bool bStartServer)
    {
        if (bStartServer)
        {
            TcpListener tlToClient = TcpListener.Create(2868);
            tlToClient.Start();
            DoClientService(tlToClient);
            System.Console.WriteLine("start on port 2868...");
            for (int i = 0; i < 10; i++)
            {
                Console.ReadLine();
                GC.Collect();
            }
        }
        else
        {
            for (int i = 0; i < 50000; i++)
            {
                Socket s = new Socket(SocketType.Stream, ProtocolType.Tcp);
                s.Connect("127.0.0.1", 2868);
                s.Shutdown(SocketShutdown.Both);
                s.Close();
            }
            System.Console.WriteLine("all done");
            Console.ReadLine();
        }
    }

    static async Task DoClientService(TcpListener tlToClient)
    {
        while (true)
            Receive(await tlToClient.AcceptSocketAsync().ConfigureAwait(false));
    }

    static async Task Receive(Socket socket)
    {
        while (true)
        {
            await Task.Delay(50).ConfigureAwait(false);
            var read = await socket.ReceiveAsync(new ArraySegment<byte>(new byte[8192], 0, 8192), SocketFlags.None).ConfigureAwait(false);
            if (read == 0)
            {
                socket.Shutdown(SocketShutdown.Both);
                socket.Dispose();
                return;
            }
        }
    }
}

As the code shows, After server is listening on port 2868, the test client will initiate 50,000 TCP connections continuely, and these connections will be closed after connecting succeed. When the server receiving buffer is set to 8,192 the server will take 800MB memory after the test ends.

I suspect that there is a bug in the internal implementation of Socket's extension method ReceiveAsync which lead to the reveive buffer area can't be released.

The code has been simplified, it will only test the memory leak problem.

Any help would be appreciated!

like image 712
zts9989 Avatar asked Jan 01 '26 13:01

zts9989


1 Answers

You should create one buffer and reuse it. Garbage collection is not aggressive, which would explain the leak. More specifically, this line var Buffer = new ArraySegment<byte>(new byte[8192], 0, 8192);

should be out of your while loop and it should be passed into ReciveAsync.

like image 168
Alex Williams Avatar answered Jan 03 '26 03:01

Alex Williams



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!