Skip to content

C# memory leak with multiple channels #14021

@jskeet

Description

@jskeet
  • Grpc.Core v1.8.0 and 1.8.3 behave very badly; Grpc.Core v1.7.3 is somewhat better (details below)
  • Windows 10

Simple repro using Google.Cloud.Speech.V1 library just for the generated protos and gRPC code:

using Google.Apis.Auth.OAuth2;
using Google.Cloud.Speech.V1;
using Grpc.Auth;
using Grpc.Core;
using System;

public class Test
{
    public static void Main(string[] args)
    {
        GoogleCredential googleCredential = GoogleCredential.GetApplicationDefault();
        ChannelCredentials channelCreds = googleCredential.ToChannelCredentials();
        for (int i = 0; i < 100; ++i)
        {
            var channel = new Channel(SpeechClient.DefaultEndpoint.Host, channelCreds);
            var speech = new Speech.SpeechClient(channel);
            var call = speech.StreamingRecognize();
            call.RequestStream.CompleteAsync().Wait();
            call.ResponseStream.MoveNext(System.Threading.CancellationToken.None).Wait();
            channel.ShutdownAsync().Wait();
            Console.WriteLine($"{i}: {GC.GetTotalMemory(true)}");
        }
    }
}

Project file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Google.Cloud.Speech.V1" Version="1.0.0" />
    <PackageReference Include="Grpc.Core" Version="1.8.3" />
  </ItemGroup>
</Project>

Output when using Grpc.Core v1.7.x:

0: 376984
1: 382392
2: 382392
3: 382960
4: 382960
5: 382960
6: 382960
7: 382960
8: 382960
9: 382960
(stable)

When using Grpc.Core v1.8.x:

0: 461312
1: 553512
2: 640344
3: 728456
4: 815368
5: 902816
6: 990232
7: 1077648
8: 1164728
9: 1250880
(keeps increasing)

Now even 1.7.x isn't perfect: if you comment out the call to call.ResponseStream.MoveNext(System.Threading.CancellationToken.None).Wait();, there's still a small leak, although it's much better than with 1.8.x:

0: 376976
1: 382504
2: 383136
3: 383280
4: 383424
5: 383648
6: 383792
7: 383936
8: 384080
9: 384224

Using a single channel instead of multiple channels avoids the leak even with 1.8.x, and even without calling MoveNext() to observe the end of the stream.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions