SpanNetty icon indicating copy to clipboard operation
SpanNetty copied to clipboard

Memory leaks after closing the connection

Open FabianHummel opened this issue 2 years ago • 0 comments

I'm trying to create a "ping" functionality in a messaging client, which requires to continuously connect to a list of servers over a period of time. I noticed that the client crashes after a few minutes because of a heap overflow. After calling await group.ShutdownGracefullyAsync();, shouldn't the memory be freed again? Why does that not happen?

Server Code (default code from example)
class Program
{
  static async Task Main(string[] args)
  {
      var bossGroup = new MultithreadEventLoopGroup(1);
      var workerGroup = new MultithreadEventLoopGroup();
      
      try
      {
          var bootstrap = new ServerBootstrap();
          bootstrap
              .Group(bossGroup, workerGroup)
              .Channel<TcpServerSocketChannel>()
              .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
              {
                  channel.Pipeline.AddLast(
                      new StringEncoder(),
                      new StringDecoder(),
                      new TelnetServerHandler());
              }));

          var bootstrapChannel = await bootstrap.BindAsync(3000);
          Console.WriteLine("Server started on port 3000");
          await Task.Delay(-1);
          await bootstrapChannel.CloseAsync();
      }
      finally
      {
          Task.WaitAll(bossGroup.ShutdownGracefullyAsync(), workerGroup.ShutdownGracefullyAsync());
      }
  }
}
using System;
using System.Net;
using System.Threading.Tasks;
using DotNetty.Transport.Channels;

public class TelnetServerHandler : SimpleChannelInboundHandler<string>
{
  public override void ChannelActive(IChannelHandlerContext context)
  {
      context.WriteAsync($"Welcome to {Dns.GetHostName()} !");
      context.WriteAndFlushAsync($"It is {DateTime.Now} now !");
  }

  protected override void ChannelRead0(IChannelHandlerContext context, string message)
  {
      context.CloseAsync();
  }

  public override void ChannelReadComplete(IChannelHandlerContext context)
  {
      context.Flush();
  }

  public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
  {
      Console.WriteLine($"{exception}");
      context.CloseAsync();
  }

  public override bool IsSharable => true;
}

This is the client code. It connects to the server 10 times and closes all of its connections. The memory (16mb) allocated however still remain unfreed after closing.

using DotNetty.Codecs;
using DotNetty.Handlers.Logging;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System;
using System.Threading.Tasks;

public static class Program
{
    private static async Task Main()
    {
        await Task.Delay(2000);
        for (var i = 0; i < 10; i++)
        {
            int copy = i;
            new Action(async () =>
            {
                var group = new MultithreadEventLoopGroup();
                var bootstrap = new Bootstrap()
                    .Group(group)
                    .Channel<TcpSocketChannel>()
                    .Handler(new ActionChannelInitializer<IChannel>(_ =>
                    {
                        Console.Out.WriteLine($"Connected {copy}");
                    }));

                await bootstrap.ConnectAsync(
                    IPAddress.Parse("127.0.0.1"),
                    3000);

                await group.ShutdownGracefullyAsync();
                Console.Out.WriteLine($"Shutdown {copy}");
            })();
            await Task.Delay(1000);
        }

        await Task.Delay(-1);
    }
}

https://github.com/cuteant/SpanNetty/assets/98157550/c17b163b-66dd-4964-9317-cc224e87fc1c

FabianHummel avatar Sep 05 '23 14:09 FabianHummel