TcpListener的异步调用内存泄漏---最近测试结果,没有泄露

我后来加大了client的连接/断开的次数(500,1000),Server端的连接被释放了。

这说明:

  1. 此代码是 可以正常工作的。

  2.TcpListener/TcpListener的async的使用上,编译器生成的代码并没有在socket关闭的时候显式调用Disopose(),Dispose()在GC的时候被调用的,个人猜测可能是Pooling,也可能是生成的代码不够漂亮。

--------------------------------------------

最近想写个socket通信的小程序,我相信大凡用过async/await特性的人再也不会去写APM、EAP之类的代码,tcp层的异步通信网上也有很多例子,MSDN上的例子如下: https://msdn.microsoft.com/en-us/magazine/dn605876.aspx , 我参考这个例子写了个Demo,结果发现有内存泄漏,细想应该是某些地方用的不对了,如果有哪位大神看出来,请帮忙指正:

Server端代码:

我们可以看到Tcp上使用TcpListener,并且异步的实现实在NetworkStream上。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace AsyncTcpListenerServer
{
using System.IO;
class Program
{
static void Log(string msg)
{
Console.WriteLine($"Thread:{Thread.CurrentThread.ManagedThreadId} {msg}");
} static void Main(string[] args)
{
ProcessConnectClient();
Log("Listened success.");
Console.ReadLine();
} static async void ProcessConnectClient()
{
IPAddress ipAddress = IPAddress.Loopback;
TcpListener listener = new TcpListener(ipAddress, );
listener.Start(); Log("Begin start listen..."); while (true)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
Log($"listened: {client.Client.RemoteEndPoint}");
ProcessReceiveData(client); }
catch (Exception ex)
{
Console.WriteLine("error:" + ex.ToString());
}
}
} static async void ProcessReceiveData(TcpClient client)
{
//client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
IPEndPoint endPoint = (IPEndPoint)client.Client.RemoteEndPoint;
string info = client.Client.RemoteEndPoint.ToString();
try
{
#region
//using (NetworkStream networkStream = client.GetStream())
//{
// byte[] buffer = new byte[client.ReceiveBufferSize];
// while (true)
// {
// int byteRead = await networkStream.ReadAsync(buffer, 0, client.ReceiveBufferSize).ConfigureAwait(false);
// if (byteRead <= 0)
// {
// break;
// }
// Log($" IP:{endPoint.Address} Port:{endPoint.Port} received {byteRead} bytes");
// }
//}
#endregion #region MyRegion
using (NetworkStream networkStream = client.GetStream())
using (MemoryStream memoryStream = new MemoryStream())
{
byte[] buffer = new byte[client.ReceiveBufferSize];
while (true)
{
int byteRead = await networkStream.ReadAsync(buffer, , client.ReceiveBufferSize).ConfigureAwait(false);
if (byteRead <= )
{
break;
}
await memoryStream.WriteAsync(buffer, , byteRead).ConfigureAwait(false);
byte[] bytes = memoryStream.ToArray();
memoryStream.Seek(, SeekOrigin.Begin); string str = Encoding.ASCII.GetString(bytes); Log($" IP:{endPoint.Address} Port:{endPoint.Port} {str}");
}
}
#endregion #region MyRegion
//using (NetworkStream networkStream = client.GetStream())
//using (StreamReader reader = new StreamReader(networkStream))
//{
// while (true)
// {
// string msg = await reader.ReadLineAsync().ConfigureAwait(false);
// if (msg == null)
// {
// break;
// } // Log($" IP:{endPoint.Address} Port:{endPoint.Port} {msg}");
// }
//}
#endregion }
catch (Exception ex)
{
Log(ex.ToString());
}
finally
{
Console.WriteLine($"client closed!");
client.Close();
}
}
}
}

Client端代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace AsyncTcpClientClient
{
class Program
{
static void Main(string[] args)
{
Task t = Sending();
t.Wait();
Console.WriteLine("OK client");
} static async Task Sending()
{
TcpClient client = new TcpClient();
try
{
await client.ConnectAsync(IPAddress.Loopback, );
using (NetworkStream stream = client.GetStream())
{
int i = ;
while (stream.CanWrite)
{
i++;
if (i>)
{
break;
}
string msg = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
byte[] ba = Encoding.Default.GetBytes(msg);
await stream.WriteAsync(ba, , ba.Length);
//System.IO.StreamWriter writer = new System.IO.StreamWriter(stream);
//writer.AutoFlush = true;
//await writer.WriteLineAsync(msg); Console.WriteLine($"Send...{msg}");
Thread.Sleep();
}
} }
catch (Exception ex)
{
Console.WriteLine(ex); }
finally
{
if (client.Connected)
{
client.Close();
}
}
}
}
}

写法是特别简单,但是。。。

开启Server端后,运行Client3次端,Server端的显示:

TcpListener的异步调用内存泄漏---最近测试结果,没有泄露

可以看到通信成功,此时抓取Dump,分析结果如下:

Microsoft (R) Windows Debugger Version 10.0.10240.9 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.

Loading Dump File [C:\Users\StevenChen\AppData\Local\Temp\AsyncTcpListenerServer (8).DMP]

User Mini Dump File with Full Memory: Only application data is available

WARNING: Minidump contains unknown stream type 0x15

WARNING: Minidump contains unknown stream type 0x16

Symbol search path is: *** Invalid ***

****************************************************************************

* Symbol loading may be unreliable without a symbol search path.           *

* Use .symfix to have the debugger choose a symbol path.                   *

* After setting your symbol path, use .reload to refresh symbol locations. *

****************************************************************************

Executable search path is:

Windows 10 Version 10240 MP (8 procs) Free x64

Product: WinNt, suite: SingleUserTS

Built by: 10.0.10240.16384 (th1.150709-1700)

Machine Name:

Debug session time: Tue Dec 15 17:29:05.000 2015 (UTC + 8:00)

System Uptime: 1 days 8:03:25.088

Process Uptime: 0 days 0:00:49.000

.....................................

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ntdll.dll -

************* Symbol Loading Error Summary **************

Module name            Error

ntdll                  The system cannot find the file specified

You can troubleshoot most symbol related issues by turning on symbol loading diagnostics (!sym noisy) and repeating the command that caused symbols to be loaded.

You should also verify that your symbol search path (.sympath) is correct.

*** ERROR: Symbol file could not be found.  Defaulted to export symbols for KERNELBASE.dll -

ntdll!ZwReadFile+0xa:

00007ff9`313135da c3              ret

0:000> .symfix d:\symbols

0:000> .loadby sos clr

0:000> !fq

No .natvis files found at C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\Visualizers.

c0000005 Exception in C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.fq debugger extension.

PC: 00007ff8`e01b2085  VA: 00000000`00000000  R/W: 0  Parameter: 00000000`00000000

0:000> !fq

SyncBlocks to be cleaned up: 0

Free-Threaded Interfaces to be released: 0

MTA Interfaces to be released: 0

STA Interfaces to be released: 0

----------------------------------

generation 0 has 123 finalizable objects (0000001d743340e0->0000001d743344b8)

generation 1 has 0 finalizable objects (0000001d743340e0->0000001d743340e0)

generation 2 has 0 finalizable objects (0000001d743340e0->0000001d743340e0)

Ready for finalization 0 objects (0000001d743344b8->0000001d743344b8)

Statistics for all finalizable objects (including all objects ready for finalization):

MT    Count    TotalSize Class Name

00007ff91a6f2ec0        1           32 Microsoft.Win32.SafeHandles.SafeFileMappingHandle

00007ff91a6f2e30        1           32 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle

00007ff91a6eab30        1           32 Microsoft.Win32.SafeHandles.SafeWaitHandle

00007ff91a6d1028        1           32 Microsoft.Win32.SafeHandles.SafePEFileHandle

00007ff91a6c1190        1           32 System.Threading.Gen2GcCallback

00007ff91a685878        1           40 System.Threading.RegisteredWaitHandleSafe

00007ff9134adbe8        3          120 System.Net.Sockets.TcpClient

00007ff91a6f0fe8        4          128 Microsoft.Win32.SafeHandles.SafeFileHandle

00007ff91a6d0fc0        2          128 System.Threading.ReaderWriterLock

00007ff91a6d3ff0        2          176 System.Diagnostics.Tracing.EventSource+OverideEventProvider

00007ff91a6ac5e8        1          184 System.Threading.PinnableBufferCacheEventSource

00007ff91a690ab8        1          184 System.Threading.Tasks.TplEtwProvider

00007ff9134ddac8        6          192 System.Net.SafeNativeOverlapped

00007ff9134d5790        3          192 System.Net.Sockets.NetworkStream

00007ff91a6de1e8        2          208 System.IO.FileStream

00007ff9134dda38        6          240 System.Net.SafeCloseSocket

00007ff9134dd820        8          256 System.Net.SafeCloseSocket+InnerSafeCloseSocket

00007ff91a6eee60        3          288 System.Threading.Thread

00007ff91a6d00f8        9          288 Microsoft.Win32.SafeHandles.SafeRegistryHandle

00007ff9134d6aa8        6          288 System.Net.Sockets.OverlappedCache

00007ff91a6e9a20       14          336 System.WeakReference

00007ff9134b60f0        3          528 System.Net.Sockets.AcceptOverlappedAsyncResult

00007ff91a6d3148       21          672 Microsoft.Win32.SafeHandles.SafeAccessTokenHandle

00007ff9134dc7c0        5          680 System.Net.Sockets.Socket

00007ff9134d7368       18         3312 System.Net.Sockets.OverlappedAsyncResult

Total 123 objects

0:000> !dumpheap -mt 00007ff9134dc7c0

Address               MT     Size

0000001d00034540 00007ff9134dc7c0      136    
0000001d00040110 00007ff9134dc7c0      136    
0000001d00052de0 00007ff9134dc7c0      136    
0000001d000680d8 00007ff9134dc7c0      136    
0000001d0007a780 00007ff9134dc7c0      136

Statistics:

              MT    Count    TotalSize Class Name

00007ff9134dc7c0        5          680 System.Net.Sockets.Socket

Total 5 objects

0:000> !gcroot 0000001d00034540

HandleTable:

0000001d71741350 (strong handle)

-> 0000001d0003be08 System.Threading._ThreadPoolWaitOrTimerCallback

-> 0000001d00034540 System.Net.Sockets.Socket

0000001d71741be0 (async pinned handle)

    -> 0000001d00052860 System.Threading.OverlappedData

-> 0000001d0007a6d0 System.Net.Sockets.AcceptOverlappedAsyncResult

-> 0000001d00034540 System.Net.Sockets.Socket

Found 2 unique roots (run '!GCRoot -all' to see all roots).

0:000> !gcroot 0000001d00040110

Finalizer Queue:

0000001d00040110

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00040328

    -> 0000001d00040328 System.Net.Sockets.TcpClient

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00040c88

    -> 0000001d00040c88 System.Net.Sockets.NetworkStream

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00050f20

    -> 0000001d00050f20 System.Net.Sockets.OverlappedAsyncResult

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00054ba0

    -> 0000001d00054ba0 System.Net.Sockets.OverlappedAsyncResult

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00054ff8

    -> 0000001d00054ff8 System.Net.Sockets.OverlappedAsyncResult

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00055450

    -> 0000001d00055450 System.Net.Sockets.OverlappedAsyncResult

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d000558a8

    -> 0000001d000558a8 System.Net.Sockets.OverlappedAsyncResult

-> 0000001d00040110 System.Net.Sockets.Socket

0000001d00055d00

    -> 0000001d00055d00 System.Net.Sockets.OverlappedAsyncResult

-> 0000001d00040110 System.Net.Sockets.Socket

Warning: These roots are from finalizable objects that are not yet ready for finalization.

This is to handle the case where objects re-register themselves for finalization.

These roots may be false positives.

Found 9 unique roots (run '!GCRoot -all' to see all roots).

0:000> !dumpheap -mt 00007ff9134d7368  
         Address               MT     Size

0000001d00050f20 00007ff9134d7368      184    
0000001d00054ba0 00007ff9134d7368      184    
0000001d00054ff8 00007ff9134d7368      184    
0000001d00055450 00007ff9134d7368      184    
0000001d000558a8 00007ff9134d7368      184    
0000001d00055d00 00007ff9134d7368      184    
0000001d00067c60 00007ff9134d7368      184    
0000001d00068a00 00007ff9134d7368      184    
0000001d00068e58 00007ff9134d7368      184    
0000001d000692b0 00007ff9134d7368      184    
0000001d00069708 00007ff9134d7368      184    
0000001d00069b60 00007ff9134d7368      184    
0000001d0007a308 00007ff9134d7368      184    
0000001d0007aea0 00007ff9134d7368      184    
0000001d0007b2f8 00007ff9134d7368      184    
0000001d0007b750 00007ff9134d7368      184    
0000001d0007bba8 00007ff9134d7368      184    
0000001d0007c000 00007ff9134d7368      184

Statistics:

              MT    Count    TotalSize Class Name

00007ff9134d7368       18         3312 System.Net.Sockets.OverlappedAsyncResult

Total 18 objects

0:000> !gcroot 0000001d00054ff8

Finalizer Queue:

0000001d00054ff8

-> 0000001d00054ff8 System.Net.Sockets.OverlappedAsyncResult

Warning: These roots are from finalizable objects that are not yet ready for finalization.

This is to handle the case where objects re-register themselves for finalization.

These roots may be false positives.

Found 1 unique roots (run '!GCRoot -all' to see all roots).

我们能够比较明显的看出有些对象没有释放,这些对象的root都指向System.Net.Sockets.OverlappedAsyncResult,但是很奇怪System.Net.Sockets.OverlappedAsyncResult本身也是一个根。

上一篇:Dart 创建List


下一篇:jquery的隐藏