grpc / grpc-dotnet

gRPC for .NET
Apache License 2.0
4.17k stars 768 forks source link

Server-side memory leaks #2203

Closed zeke202207 closed 1 year ago

zeke202207 commented 1 year ago

What version of gRPC and what language are you using?

What operating system (Linux, Windows,...) and version?

Windows 10 [version 10.0.19045.3208]

Scenario

Following the example from the official website, a simple demo was written. I found that grpc-service memory kept going up and not down, even when I closed the calling client, the memory still did not go down

server code


public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
    builder.WebHost.ConfigureKestrel(options =>
    {
        options.ListenAnyIP(4999, listenOptions => listenOptions.Protocols = HttpProtocols.Http2);
    });

    builder.Services.AddGrpc();

    var app = builder.Build();

    app.MapGrpcService<GreeterService>();

    app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. ");

    app.Run();
}

public class GreeterService : Greeter.GreeterBase
{
    public GreeterService()
    {
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        Console.WriteLine($"Starting call. Request: {request}");
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

> client code 
static void Main(string[] args)
{
    using var channel = GrpcChannel.ForAddress("http://127.0.0.1:4999");
    var client = new Greeter.GreeterClient(channel);

    //Press Enter and call grpc service 100 times
    while (string.IsNullOrWhiteSpace(Console.ReadLine()))
    {
        for(int i = 0; i < 100; i++)
        {
            var reponse = client.SayHello(new HelloRequest() { Name =$"zeke-{i}" });
        }
    }

    Console.WriteLine("bye!");
    Console.ReadLine();
}

 > protos
syntax = "proto3";

option csharp_namespace = "GrpcService1";

package greet;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings.
message HelloReply {
string message = 1;
}


  - After the service is started, the memory is monitored as follows

    | name      | memory |
    | --------- | ------ |
    | GrpcService1.exe      | 44,568 k       |

  - The client calls 500 times for the first time, the memory check is as follows

    | name      | memory |
    | --------- | ------ |
    | GrpcService1.exe      | 49,188 k       |

  - The client calls 500 times for the second time, the memory is checked as follows

    | name      | memory |
    | --------- | ------ |
    | GrpcService1.exe      | 49,316 k       |

  - After repeating the previous step N times, close the client and check for memory as follows

    | name      | memory |
    | --------- | ------ |
    | GrpcService1.exe      | 55,840 k       |   

## As you can see, the memory keeps growing and is not reclaimed

I want to be consulted. Is there a problem with my usage? or Is there a memory leak in grpc?.<br/>
Look forward to your reply,Thanks for helping!
luchunminglu commented 1 year ago

May be just console, it display more lines, use more memory.

zeke202207 commented 1 year ago

May be just console, it display more lines, use more memory.

Even so, when I call the service interface for a long time, I find that the memory is still growing, and there is no decline in reclaim.is this behavior normal? Thank you and look forward to your reply @luchunminglu

zeke202207 commented 1 year ago

I did a test today, the early memory growth rate is very large, after running for a period of time, the memory growth rate is reduced, but the memory is still growing, not always stable or decline.

gfoidl commented 1 year ago

This is the normal behavior of the .NET GC.

In the client do something like

DateTime start = DateTime.Now;

while (DateTime.Now <= start.AddSeconds(20))
{
    for (int i = 0; i < 100; i++)
    {
        HelloReply reponse = client.SayHello(new HelloRequest() { Name = $"zeke-{i}" });
        Console.WriteLine(reponse.Message);
    }
}

then -- when the GC determines to run -- the memory usage can drop...see link above.

Under a profiler (the one from VS) it looks on my machine like grafik

The red vertical bars show when the GC kicks in to collect objects. Can also be seen in the "Live Objects" graph above.

zeke202207 commented 1 year ago

Thank you so much @gfoidl