Aleks
Aleks

Reputation: 1689

SerialPort SerialStream leaking memory in read loop

I'm trying to use the following to continuously read data from a serial port:

var serialPort = new SerialPort("COM7", 38400, Parity.None, 8, StopBits.One);

var buffer = new byte[256];

Action read = null;

AsyncCallback callback = delegate (IAsyncResult ar)
{
    int len = serialPort.BaseStream.EndRead(ar);

    // do something with data

    read();
};

read = delegate
{
    serialPort.BaseStream.BeginRead(buffer, 0, buffer.Length, callback, null);
};

serialPort.Open();

while (true)
{
    read();

    //Thread.Sleep(100);
}

This leaks memory with an ever-increasing number of the following objects:

The objects persist even after a garbage collection pass.

The sample above is a minimal reproducible example. I've taken out the "do something with data" because the problem occurs with or without any data handling at that point.

The Thread.Sleep in the while loop only slows down memory consumption. I've left it out so that it clearly shows the problem. The above sample will consume approximately 650mb in 20 seconds on my machine.

The problem occurs in both .NET Core 3.1 and .NET Framework 4.8.

I'm sure I'm doing something wrong, I just can't see what it is at this point.

Upvotes: 1

Views: 329

Answers (1)

user585968
user585968

Reputation:

This leaks memory with an ever-increasing...

Quite simply because you are infinitely looping BeginRead operations before the existing one has completed via:

while (true)
{
    read(); // <-- this delegate calls `BeginRead` thus starting ANOTHER verlapped read operation

    //Thread.Sleep(100);
}

Change it to something like:

serialPort.Open();
read(); // kick off initial read

while (/* some condition*/)
{
}

// quit

Your callback is doing the right thing by ensuring that another overlapped read operation is only commenced once the prior completes:

AsyncCallback callback = delegate (IAsyncResult ar)
{
    int len = serialPort.BaseStream.EndRead(ar);

    // do something with data

    read(); // <--- correctly initiates another read since the existing is complete
};

Upvotes: 1

Related Questions