• 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, 8099);
                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, 0, client.ReceiveBufferSize).ConfigureAwait(false);
                            if (byteRead <= 0)
                            {
                                break;
                            }
                            await memoryStream.WriteAsync(buffer, 0, byteRead).ConfigureAwait(false);
                            byte[] bytes = memoryStream.ToArray();
                            memoryStream.Seek(0, 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, 8099);
                    using (NetworkStream stream = client.GetStream())
                    {
                        int i = 0;
                        while (stream.CanWrite)
                        {
                            i++;
                            if (i>5)
                            {
                                break;
                            }
                            string msg = System.Diagnostics.Process.GetCurrentProcess().ProcessName;
                            byte[] ba = Encoding.Default.GetBytes(msg);
                            await stream.WriteAsync(ba, 0, ba.Length);
                            //System.IO.StreamWriter writer = new System.IO.StreamWriter(stream);
                            //writer.AutoFlush = true;
                            //await writer.WriteLineAsync(msg);
    
                            Console.WriteLine($"Send...{msg}");
                            Thread.Sleep(1000);                       
                        }
                    }
    
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                    
                }
                finally
                {
                    if (client.Connected)
                    {
                        client.Close();
                    }
                }
            }
        }
    }

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

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

    abcdefg

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

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


    Loading Dump File [C:UsersStevenChenAppDataLocalTempAsyncTcpListenerServer (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 Kits10Debuggersx64Visualizers.
    c0000005 Exception in C:WindowsMicrosoft.NETFramework64v4.0.30319sos.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本身也是一个根。

  • 相关阅读:
    golang ssh 相关
    javascript 常用正则表达式收集
    Mac下 shell文件双击可执行怎么写
    Python常用插件之BeautifulSoup4使用
    Python常用插件之Requests使用
    JavaScript学习-WeakSet
    javascript学习-Set
    Vue-大型项目下路由的模块拆分
    360兼容模式ie10不支持includes方法
    360兼容模式ie10及以下版本map对象和Set对象没有定义
  • 原文地址:https://www.cnblogs.com/StevenChennet/p/5049012.html
Copyright © 2020-2023  润新知