Manually unpinning a byte[] in C#?
Asked Answered
I

1

3

In the following code, it seems that the client.Connect.Receive is pinning the "byte[] result" permanently, causing the memory to never be freed (as it's always pinned). I'm looking for a way to tell C# that result no-longer needs to be pinned after it's usage in this.OnReceive, but I can't find the built-in function or keyword to do this.

Does anyone know how I can get C# to unpin the byte[] array? (this is one of the sources of memory leaks in my C# application)

this.m_TcpListener = new TcpListener(this.p_TcpEndPoint.Port);
this.m_TcpThread = new Thread(delegate()
{
    try
    {
        this.m_TcpListener.Start();
        while (this.p_Running)
        {
            TcpClient client = this.m_TcpListener.AcceptTcpClient();
            new Thread(() =>
                {
                    try
                    {
                        // Read the length header.
                        byte[] lenbytes = new byte[4];
                        int lbytesread = client.Client.Receive(lenbytes, 0, 4, SocketFlags.None);
                        if (lbytesread != 4) return; // drop this packet :(
                        int length = System.BitConverter.ToInt32(lenbytes, 0);
                        int r = 0;

                        // Read the actual data.
                        byte[] result = new byte[length];
                        while (r < length)
                        {
                            int bytes = client.Client.Receive(result, r, length - r, SocketFlags.None);
                            r += bytes;
                        }

                        Console.WriteLine("Received TCP packet from " + (client.Client.RemoteEndPoint as IPEndPoint).Address.ToString() + ".");
                        this.OnReceive(client.Client.RemoteEndPoint as IPEndPoint, result, length);
                    }
                    catch (SocketException)
                    {
                        // Do nothing.
                    }

                    client.Close();                                
                }).Start();
            //this.Log(LogType.DEBUG, "Received a message from " + from.ToString());
        }
    }
    catch (Exception e)
    {
        if (e is ThreadAbortException)
            return;
        Console.WriteLine(e.ToString());
        throw e;
    }
}
);
this.m_TcpThread.IsBackground = true;
this.m_TcpThread.Start();
Ido answered 28/1, 2011 at 16:10 Comment(3)
Never write throw e;. Instead, write throw;. Otherwise, you'll destroy the stack trace.Fourth
When you say "it seems", how do you know this? What evidence leads you to assume that the memory stays pinned?Canter
The framework code has no known leak like this, the buffer gets pinned with the fixed keyword. You seem to be playing some dangerous games with Thread.Abort(), I'd look there.Corin
A
5

You can pin/unpin it yourself, thusly:

//Pin it 
GCHandle myArrayHandle = GCHandle.Alloc(result,GCHandleType.Pinned);
//use array
while (r < length)
{
    int bytes = client.Client.Receive(result, r, length - r, SocketFlags.None);
    r += bytes;
}
//Unpin it
myArrayHandle.Free();

But I'd personally be pretty surprised that client.Connect.Receive pins it "for all time". I've used it before (as I'm sure many have) and not run into an issue of this type. Alternately, if you're certain that's the problem, then instead of allocating a new result array each time, you can re-use one across the entire while loop (allocate it up where you start the listener, and only use lenbytes bytes each time).

Adulterant answered 28/1, 2011 at 16:38 Comment(4)
While this didn't solve the issue I was having, it does seem to answer the question of manual unpinning of an object, so I've marked it as correct for that reason.Ido
@Adulterant Thanks for a great post, it's one of the only places I've found that deals with manual unpinning. I have a situation where I have byte[]'s getting pinned, and I would like to unpin them. The only problem is I am not the one pinning them, so I don't have a nice clean myArrayHandle to invoke .Free() on. My buffers are getting pinned behind the scenes by the runtime in the process of doing aynchronous network. Will manually pinning it override background pinning, thus letting me manually unpin?Rhoades
Huh? This does not unpin the object, of course. Only when the last pinning handle is destroyed, the pinning is removed.Borries
@Borries is correct in that this will only unpin arrays you have pinned; I don't believe there is a safe way to unpin arrays that other threads/modules/code have pinned, as there is no guarantee that the arrays are not still in use somehow. Typically, whoever allocates the object (pins it) should free it when they are done (unpin it). If you need to guarantee that happens, try loading the offending code in its own AppDomain and unloading it when you're done; that ought to have the intended effect.Adulterant

© 2022 - 2024 — McMap. All rights reserved.