首页 > 代码库 > SocketAsyncEventArgs的释放问题
SocketAsyncEventArgs的释放问题
起因是发现一个同事编写的程序运行两个月左右,占用了服务器20G左右的内存。用WinDbg查看发现存在大量的Async Pinned Handles,而它们的gcroot都来自于SocketAsyncEventArgs。下面是场景的简易模拟代码(为了说明问题添加了手动GC):
for (var i = 0; i < 1000; ++i) { var endPoint = new IPEndPoint(IPAddress.Parse(host), port); var socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); socket.ReceiveBufferSize = bufferSize; socket.SendBufferSize = bufferSize; var iocp = new SocketAsyncEventArgs(); iocp.Completed += new EventHandler<SocketAsyncEventArgs>(OnIoSocketCompleted); iocp.SetBuffer(new Byte[bufferSize], 0, bufferSize); iocp.AcceptSocket = socket; try { socket.Connect(endPoint); Console.WriteLine(i); } catch (SocketException ex) { Console.WriteLine(ex.Message); } socket.Close(); //iocp.Dispose(); } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
SocketAsyncEventArgs的SetBuffer函数内部会pin住buffer数据(查阅SetBufferInternal实现),它的析构函数会调用FreeOverlapped函数释放资源。而问题就是在于FreeOverlapped函数的实现和传递参数。来看一下Dispose、~SocketAsyncEventArgs的实现:
public void Dispose() { this.m_DisposeCalled = true; if (Interlocked.CompareExchange(ref this.m_Operating, 2, 0) != 0) { return; } this.FreeOverlapped(false); GC.SuppressFinalize(this); }
~SocketAsyncEventArgs() { this.FreeOverlapped(true); }
两者都会调用FreeOverlapped函数释放,但是一个传递了false、一个传递了true参数。再来看FreeOverlapped实现:
private void FreeOverlapped(bool checkForShutdown) { if (!checkForShutdown || !NclUtilities.HasShutdownStarted) { if (this.m_PtrNativeOverlapped != null && !this.m_PtrNativeOverlapped.IsInvalid) { this.m_PtrNativeOverlapped.Dispose(); this.m_PtrNativeOverlapped = null; this.m_Overlapped = null; this.m_PinState = SocketAsyncEventArgs.PinState.None; this.m_PinnedAcceptBuffer = null; this.m_PinnedSingleBuffer = null; this.m_PinnedSingleBufferOffset = 0; this.m_PinnedSingleBufferCount = 0; } if (this.m_SocketAddressGCHandle.IsAllocated) { this.m_SocketAddressGCHandle.Free(); } if (this.m_WSAMessageBufferGCHandle.IsAllocated) { this.m_WSAMessageBufferGCHandle.Free(); } if (this.m_WSARecvMsgWSABufferArrayGCHandle.IsAllocated) { this.m_WSARecvMsgWSABufferArrayGCHandle.Free(); } if (this.m_ControlBufferGCHandle.IsAllocated) { this.m_ControlBufferGCHandle.Free(); } } }
用WinDbg查看gchandles统计:
Statistics: MT Count TotalSize Class Name 000007fb1bdb6ae8 1 24 System.Object 000007fb1bdb6b80 1 48 System.SharedStatics 000007fb1bdb7f58 1 64 System.Security.PermissionSet 000007fb1bdb6a10 1 160 System.ExecutionEngineException 000007fb1bdb6998 1 160 System.StackOverflowException 000007fb1bdb6920 1 160 System.OutOfMemoryException 000007fb1bdb6738 1 160 System.Exception 000007fb1bdb7b90 2 192 System.Threading.Thread 000007fb1bdb6c40 1 216 System.AppDomain 000007fb1bdb6a88 2 320 System.Threading.ThreadAbortException 000007fb1ae19770 6 336 System.Net.Logging+NclTraceSource 000007fb1bdbf958 3 480 System.RuntimeType+RuntimeTypeCache 000007fb1ae19900 6 480 System.Diagnostics.SourceSwitch 000007fb1bd64458 6 34520 System.Object[] 000007fb1b6f5e40 1000 112000 System.Threading.OverlappedData Total 1033 objects Handles: Strong Handles: 12 Pinned Handles: 5 Async Pinned Handles: 1000 Weak Long Handles: 3 Weak Short Handles: 13
确实存在1000个Async Pinned Handles,也就是说无法通过SocketAsyncEventArgs的析构函数释放SafeNativeOverlapped相关的资源。将示例代码的"iocp.Dispose();"注释去除,并重新执行再次查看gchandles:
Statistics: MT Count TotalSize Class Name 000007fb1bdb6ae8 1 24 System.Object 000007fb1bdb6b80 1 48 System.SharedStatics 000007fb1bdb7f58 1 64 System.Security.PermissionSet 000007fb1bdb6a10 1 160 System.ExecutionEngineException 000007fb1bdb6998 1 160 System.StackOverflowException 000007fb1bdb6920 1 160 System.OutOfMemoryException 000007fb1bdb6738 1 160 System.Exception 000007fb1bdb7b90 2 192 System.Threading.Thread 000007fb1bdb6c40 1 216 System.AppDomain 000007fb1bdb6a88 2 320 System.Threading.ThreadAbortException 000007fb1ae19770 6 336 System.Net.Logging+NclTraceSource 000007fb1bdbf958 3 480 System.RuntimeType+RuntimeTypeCache 000007fb1ae19900 6 480 System.Diagnostics.SourceSwitch 000007fb1bd64458 6 34520 System.Object[] Total 33 objects Handles: Strong Handles: 12 Pinned Handles: 5 Weak Long Handles: 3 Weak Short Handles: 13
1000个Async Pinned Handles已不存在,但SocketAsyncEventArgs的析构函数从实现来看应该也可以完成释放,为什么失败了?
用!bpmd命令添加NclUtilities.HasShutdownStarted、 m_PtrNativeOverlapped.Dispose()的断点。
!bpmd System.dll System.Net.NclUtilities.get_HasShutdownStarted
!bpmd mscorlib.dll System.Runtime.InteropServices.SafeHandle.Dispose
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。