.NET Forum / .NET Framework / New Users / May 2005
GC and active sockets
|
|
Thread rating:  |
Arthur M. - 11 May 2005 14:24 GMT I'm using .net 2.0 beta2 and noticing some strange behaviour around socket garbage collection.
Problem: If a socket is a part of a class which has been instantiated, socket connected, and class de-referenced Socket will stay connected forever.
Environment: Sockets are doing async read/write
Question: How do i get it solved :) also - is putting something on IOCP thread will cause garbage collection to fail because of internal blocking that is going on. How can i clean up connected socket which is a member of a class which suddenly lost refernece. Under normal conditions I do clean up, however, there are scenarios where abnormal termination of a thread can cause socket's parent class loose reference.
Arthur M. - 11 May 2005 14:38 GMT Bleh... I think I found the answer as to why, no solution as of yet though....
When Socket.BeginReceive is called, buffer memory appears to be pinned, therefore it will never unpin it until some data shows and the call continues, the problem is that Socket.BeginReceive will call itself in the end of its call hence new buffer will get pinned, so unless GC tries to collect it at the moment when the call is completed but new one has not been initiated yet .... we have a hanging socket
Anyone has any idea if there is a way to get reference count to an instance of a class from .NET?
> I'm using .net 2.0 beta2 and noticing some strange behaviour around socket > garbage collection. [quoted text clipped - 13 lines] > there are scenarios where abnormal termination of a thread can cause socket's > parent class loose reference. Rob White - 11 May 2005 16:30 GMT Couldn't you just make sure that you included a distructor ~ClassName() into your object that closes the socket on it's destruction?
> Bleh... I think I found the answer as to why, no solution as of yet though.... > [quoted text clipped - 25 lines] > > there are scenarios where abnormal termination of a thread can cause socket's > > parent class loose reference. Arthur M. - 11 May 2005 16:48 GMT The destructor is there; however, since in .NET destructor = finalizer, the object has to be considered for finalization or finalizer wont run.
It seems as if GC is not considering object for finalization since there is an IOCP wait operation in progress (beginread/beginreceive). I have tried numerous ways and
The only way to interrupt the wait is to send the data to the socket as there is no native way to shutdown beginXXX once it has started.
> Couldn't you just make sure that you included a distructor ~ClassName() into > your object that closes the socket on it's destruction? [quoted text clipped - 28 lines] > > > there are scenarios where abnormal termination of a thread can cause socket's > > > parent class loose reference. Willy Denoyette [MVP] - 11 May 2005 17:19 GMT > The destructor is there; however, since in .NET destructor = finalizer, > the [quoted text clipped - 7 lines] > The only way to interrupt the wait is to send the data to the socket as > there is no native way to shutdown beginXXX once it has started. Could you please post a small but complete sample that illusrates the issue?
Willy.
Arthur M. - 11 May 2005 18:11 GMT In my specific case there is a socket server listenting on port 1111, you can use anything that accepts a connection.
Note that function openConnections() never really closes a tcp client, but instead lets them go out of scope.
Granted in true production environment this should never happen, however, garbage collection should prevent exactly that - memory/resource leak.
My original code involves working directly with sockets, to ensure it is not my mistake, I have reproduced it with .NETs internal TCPClient hence no try/catch no watches for shutdowns and buffers that are thrown away
Garbage collection timer is there to ensure garbage collection is forced to run.
Code Below:
static void openConnections() { for ( int op_counter = 0; op_counter < 30; op_counter++ ) { System.Net.Sockets.TcpClient pt = new System.Net.Sockets.TcpClient(); Singularity.Communication.Client(System.Net.IPAddress.Any, 0, System.Net.IPAddress.Loopback, 1111); pt.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 1111)); System.Net.Sockets.NetworkStream ns = pt.GetStream(); ns.BeginRead(new byte[100], 1, 0, rcvd, ns); } }
static void rcvd(IAsyncResult ar) { ((System.Net.Sockets.NetworkStream) (ar.AsyncState)).EndRead(ar); ( (System.Net.Sockets.NetworkStream) ( ar.AsyncState ) ).BeginRead(new byte[100], 1, 0, rcvd, ( (System.Net.Sockets.NetworkStream) ( ar.AsyncState ) )); }
static Timer tmr = new Timer();
static void Main() { tmr.Interval = 1000; tmr.Enabled = true; tmr.Start(); tmr.Tick += new EventHandler(tmr_Tick); //f.Show(); //f.FormClosed += new FormClosedEventHandler(f_FormClosed); //f = null; openConnections(); Application.Run(); }
static void tmr_Tick(object sender, EventArgs e) { Console.WriteLine("Collecting"); tmr.Stop(); GC.Collect(); GC.WaitForPendingFinalizers(); tmr.Start(); }
> > The destructor is there; however, since in .NET destructor = finalizer, > > the [quoted text clipped - 11 lines] > > Willy. Willy Denoyette [MVP] - 11 May 2005 20:30 GMT > In my specific case there is a socket server listenting on port 1111, you > can [quoted text clipped - 66 lines] > tmr.Start(); > } While ns goes out of scope in openConnections it is passed to BeginRead : ... ns.BeginRead(new byte[100], 1, 0, rcvd, ns);
as argument and used in rcvd, so you keep a reference to your NetworkStream (ns) the wrapped TcpClient (tp) and his underlying socket. What would you expect from the GC to free here?
Willy.
Arthur M. - 11 May 2005 22:02 GMT That was exactly my point; once an object goes out of scope and effectively is unreachable through any means available to developer, the only reference point to that object is a blocking IOCP thread.
I agree that from pure system's point of view everything works properly, reference A goes away but reference B is still there. From development stand point of view, however, once i place something into IOCP pool there is no way for me to signal interrupt therefore, there is a logical assumption that if i remove all references to the object and the only one remains is IOCP reference, it should get garbage collected. Granted, leaving objects out of scope without clean up is bad programming habbit as is, but isn't garbage collection supposed to protect against those type of mistakes?
I think: ( no refernce = garbage) equally (no reference = there is no way for a developer to access an object) and therefore (no way for a developer to access an object = garbage)
Whether it is an adjustment to GC or adjustment to IOCP implementation (give me an ability to get BeginXXX operation out of wait state) is question someone in Microsoft (in my opinion) needs to ask and answer.
> > In my specific case there is a socket server listenting on port 1111, you > > can [quoted text clipped - 76 lines] > > Willy. Willy Denoyette [MVP] - 11 May 2005 23:11 GMT Objects don't go out of scope, references do go out of scope. Your local references go out of scope, but as YOU passed a NetworkStream reference to the BeginRead delegate which posts a request to the completion thread pool which stores the NetworkStream reference for later use, you have yet another reference to the NetworkStream Object ( and as such to the TcpClient and the Socket instances), so the GC can't collect. At this point YOU say he! this is a "bad programming habit" I would like the GC to consider the NS object as garbage, I say "it's a bug in your code" and the GC can't and isn't supposed to protect you from bugs in your code.
When you're done with the TcpClient, you simply have to close it by calling TcpClient.Close(), this will effectively remove the request from the pool, close the socket (Dispose) the NetworkStream (Dispose) and the TcpClient objects.
Willy.
> That was exactly my point; once an object goes out of scope and > effectively [quoted text clipped - 111 lines] >> >> Willy. Arthur M. - 25 May 2005 01:07 GMT I think you need to re-read the thread! there is no way for ME to interrupt a function placed in IO completion pool therefore, there is no way for me to remove the reference once external conditions require me to, therefore I HAVE TO wait for IOCP thread to drop the reference before i can clean up and avoid memory leaks.
There is a logic flaw here, I cant terminate an IOCP thread until external conditions set it off and it completes. If situation is such that conditions will never arise ... memory leak. This is a BUG and if you dont consider it to be a bug with GC, it is a bug with overal automatic resource management.
With socket, I do have an ability to call close and thereby release the handle, but with external resources, it is not always possible.
> Objects don't go out of scope, references do go out of scope. > Your local references go out of scope, but as YOU passed a NetworkStream [quoted text clipped - 128 lines] > >> > >> Willy. Willy Denoyette [MVP] - 25 May 2005 12:43 GMT >I think you need to re-read the thread! there is no way for ME to >interrupt [quoted text clipped - 16 lines] > With socket, I do have an ability to call close and thereby release the > handle, but with external resources, it is not always possible. The GC has nothing to do with resource clean-up, he's only there to manage the objects stored on the managed heap, not the external resources like handles , connection, files , unmanaged memory .... Resource management is done by applying the Dispose pattern.
Please post a complete sample that illustrates the issue.
Willy. BTW functions aren't placed in IOCP pools, the callback will be executed on the IOCP thread when an event is signalled on the socket. Such event can be a close or data put on the stream by the TCP stack.
Arthur M. - 11 May 2005 18:14 GMT In my specific case there is a socket server listenting on port 1111, you can use anything that accepts a connection.
Note that function openConnections() never really closes a tcp client, but instead lets them go out of scope.
Granted in true production environment this should never happen, however, garbage collection should prevent exactly that - memory/resource leak.
My original code involves working directly with sockets, to ensure it is not my mistake, I have reproduced it with .NETs internal TCPClient hence no try/catch no watches for shutdowns and buffers that are thrown away
Garbage collection timer is there to ensure garbage collection is forced to run.
Code Below:
static void openConnections() { for ( int op_counter = 0; op_counter < 30; op_counter++ ) { System.Net.Sockets.TcpClient pt = new System.Net.Sockets.TcpClient(); Singularity.Communication.Client(System.Net.IPAddress.Any, 0, System.Net.IPAddress.Loopback, 1111); pt.Connect(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 1111)); System.Net.Sockets.NetworkStream ns = pt.GetStream(); ns.BeginRead(new byte[100], 1, 0, rcvd, ns); } }
static void rcvd(IAsyncResult ar) { ((System.Net.Sockets.NetworkStream) (ar.AsyncState)).EndRead(ar); ( (System.Net.Sockets.NetworkStream) ( ar.AsyncState ) ).BeginRead(new byte[100], 1, 0, rcvd, ( (System.Net.Sockets.NetworkStream) ( ar.AsyncState ) )); }
static Timer tmr = new Timer();
static void Main() { tmr.Interval = 1000; tmr.Enabled = true; tmr.Start(); tmr.Tick += new EventHandler(tmr_Tick); //f.Show(); //f.FormClosed += new FormClosedEventHandler(f_FormClosed); //f = null; openConnections(); Application.Run(); }
static void tmr_Tick(object sender, EventArgs e) { Console.WriteLine("Collecting"); tmr.Stop(); GC.Collect(); GC.WaitForPendingFinalizers(); tmr.Start(); }
> > The destructor is there; however, since in .NET destructor = finalizer, > > the [quoted text clipped - 11 lines] > > Willy.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|