Memory leak in SerialPort.Read?

Hi.

I communicate with the serial port on my ”PDA/smart device” to another device (custom circuit board). The PDA needs to send data at least once a second to the hardware just to confirm that there is a connection; If not the hardware shuts down. The PDA also receives information from the device witch is displayed.
The problem is that when the PDA receivea data, the application starts to leak memory. After 30 minutes to an hour the garbage collection procedure takes so much time that the communication halts for several seconds, witch in turn causes a timeout.

Could there be a problem in the SerialPort.Receive() or in the garbage collector. Most likely I have done something wrong…

Here is a simplified version of the communication, but it suffers the same problems.
If you try it, the leak could take a minute before the memory starts to leak.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace MemoryLeak
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void timer1_Tick(object sender, EventArgs e)
{
GC.Collect();
GC.WaitForPendingFinalizers();

label1.Text = "MEM:"+ (GC.GetTotalMemory(true)/1024)+ "kB";
label2.Text = "MEM:" + GC.GetTotalMemory(true) + "B";

if (serialPort1.IsOpen)
{
SendTrashToKeepCommunicationRunning(); // hardware needs to get data in order to send data.

byte[] tempArray = new byte[3000];
int bytesToRead = serialPort1.BytesToRead;
serialPort1.Read(tempArray, 0, bytesToRead);
// Process incoming data..
tempArray = null; // Should also go out of scope and be collected by the GC
}
}

private void button1_Click(object sender, EventArgs e)
{
serialPort1.Open();
timer1.Enabled = true;
}

private void button2_Click(object sender, EventArgs e)
{
timer1.Enabled = false;
serialPort1.Close();
}

private void SendTrashToKeepCommunicationRunning()
{

byte[] D = new byte[14];
D[0] = 255;
D[1] = 170;
D[2] = 31;
D[3] = 8;
D[4] = 0;
D[5] = 0;
DDevil = 0;
D[7] = 0;
DMusic = 0;
D[9] = 0;
D[10] = 0;
D[11] = 8;
// Checksum
D[12] = Convert.ToByte((D[0] + D[1] + D[2] + D[3] + D[4] + D[5] + DDevil + D[7] + DMusic + D[9] + D[10] + D[11]) / 256); // Checksum High
D[13] = Convert.ToByte((D[0] + D[1] + D[2] + D[3] + D[4] + D[5] + DDevil + D[7] + DMusic + D[9] + D[10] + D[11]) % 256); // Checksum Low

if (serialPort1.IsOpen)
{
serialPort1.Write(D, 0, 14); // send data
serialPort1.Write(D, 0, 14); // send data
}
D = null; // Should also go out of scope and be collected by the GC
}



}
}


Answer this question

Memory leak in SerialPort.Read?

  • gripusa

    First off all, Thanks for the quick respons.I had som problems understanding what the problem was. I have commented some of you answers.

    is that your code has no loops and acts only for events.
    Simplifyed example = no loops

    Real app = loops (I have a separate thread running to handle send and receive calls, = loop)

    the problem in SerialPort is, that the Read()-method calls a buffer which is everytime used from the other station until the bluetooth connection is open.
    I do not understand, could you clarify what you mean by “station”. I realise that the SerialPort has its own buffer, and returns data from it when SerialPort.Read() is called.

    So if you set the "tempArray = null" and the GC see that the other station is still using this buffer und you set this to null then you have a problem. (is not Thread safe) Thats the reason because Serial Port must be used everytime over Delegates and with the Method string serialPort.ReadToEnd();
    Still not following what you mean by “station”, and i can not find a method serialPort.ReadToString(); in C#. Could you give a short example

    Attention: read the book about WinCE because every loop in WinCE is one Loop to much. It stops the possibility to let the CPU take the Hibernate-State to save Energy. For the following Code:
    application.start()
    While !( UserPressExit ) { ... }
    application.exit()
    This is very bad because this code makes busy wating and the battery is in some hours empty.
    I see your point! My real application will have only little idle time since it is more important to lower the communication delays instead.


  • Vijay Guru Prasadh

    Tell us a bit more:
    - What's timer1's interval
    - At what rate do you see GC.GetTotalMemory() increasing
    - What do you do in "// Process incoming data"
    - What happens when you make the D[] and tempArray[] buffers class members



  • andyr2005

    Chris,

    That looked like stuff for the desktop. I have the problem in the compact framework.

    The leak is in the serialport.readexisting line.

    Thanks



  • QuinDennis

    Mike, please post your question on the .NET CF forums for help:

    http://forums.microsoft.com/MSDN/ShowForum.aspx ForumID=33&SiteID=1

    -Chris


  • Scott Boyd

    Hi Kristian

    There's a great blog post by Performance guru Rico Mariani that can help you track down your managed memory leak:

    http://blogs.msdn.com/ricom/archive/2004/12/10/279612.aspx

    Hope that helps

    -Chris


  • Michael J Brown

    Mike

    Did the article I linked to not indicate where the leak could be How are you measuring the memory leak

    -Chris


  • Fyrus

    sorry for my terrible answer. i'm busy...

    so again: you are rigth programs should have loops but in Applications on WinCE is each loop like While(true){} so led a for example a worker thread do this job terrible!

    with serial port you can use different strategien.

    1. polling: this is "while(true){...}"

    2. event driven

    use the delegate from serial port (i think it was called dataReceived) object. if you do this you can use the method readToEnd() from serial port. the method return a string and not byte. it's user friendly to read.

    bye


  • Musafir

    This thread is very interesting as when I use .NETCF2 SP1 serial port class, my application leaks a lot.

    Basically, I have a GPS device on an IPAQ 2210 connected via a serial cable into com1.

    I use the Receive event to start a new thread. Once in it, I loop round checking for data.

    While not the most elegant of ways to do it, it stops new threads being created / exited every second.

    The port.ReadExisting seems to leak a few KB every 10 seconds or so.

    Any ideas SP1 applied to VS2005 and handheld.

    Thanks

    Code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Diagnostics;
    using System.IO.Ports;
    using System.Threading;

    namespace DeviceApplication1
    {
    public partial class Form1 : Form
    {
    private SerialPort port = new SerialPort("COM1:", 57600, Parity.None, int.Parse("8"), StopBits.One);

    public Form1()
    {
    InitializeComponent();
    Start();
    }

    private void Start()
    {
    port.ReceivedBytesThreshold = 1;
    port.DtrEnable = false;
    port.Handshake = Handshake.None;
    port.WriteBufferSize = 500;
    port.WriteTimeout = 2000;
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    port.Open();
    }

    private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
    //now this thread is active, turn off threshold
    port.ReceivedBytesThreshold = 999999;

    string Buffer = "";

    try
    {
    //Just continue reading port on this new thread
    while (port.IsOpen)
    {
    Buffer = port.ReadExisting();

    Debug.WriteLine("MEM:" + (GC.GetTotalMemory(true) / 1024) + "kB");

    Thread.Sleep(50);
    }
    }
    catch (Exception ex)
    {
    Debug.WriteLine("Error:" + ex.Message);
    }
    }
    }
    }



  • AlfonsAberg

    Hmm, 1400 bytes per "tick", that doesn't jive well with the buffer sizes. I see nothing in your code that would cause this leak, are we looking at *all* the source code Email me your project if it is too large to post, |Monkeytail| = @


  • Reese Bird

    Thank you all for your time.

    Timer1 interval = 300 ms
    GC.GetTotalMemory() increases with 280 kB/minute.
    “Process incoming data” does nothing in the example.
    D[] and TempArray[] as class members does not help. Still leaking memory.


    (The real application that I have developed would benefit from real time behaviour. But since there is no guarantee for this with in .Net, I have tested several methods to get a more stabile communication delay. This is best achieved with a separate (high priority) thread instead of relying on events. When using events the time between sent protocols is very sporadic, and could easily cause the application to time out. I realise that this is not the best way to do communication, and an application without my special needs would work much smoother with an event driven communication.)


  • adorer

    The best way to communicate over serial port is to use events or delegates. The advantage is that your code has no loops and acts only for events. the problem in SerialPort is, that the Read()-method calls a buffer which is everytime used from the other station until the bluetooth connection is open.

    So if you set the "tempArray = null" and the GC see that the other station is still using this buffer und you set this to null then you have a problem. (is not Thread safe) Thats the reason because Serial Port must be used everytime over Delegates and with the Method string serialPort.ReadToEnd();

    Attention: read the book about WinCE because every loop in WinCE is one Loop to much. It stops the possibility to let the CPU take the Hibernate-State to save Energy. For the following Code:

    application.start()

    While !( UserPressExit ) { ... }

    application.exit()

    This is very bad because this code makes busy wating and the battery is in some hours empty.

    bye patrick


  • Memory leak in SerialPort.Read?