内存泄漏问题分析之非托管资源泄漏

在某次巡查生产环境监控数据的时候,发现某个程序的内存占用偏高(大于500M)。对于这个程序的作用需要简单交代一下,这个程序是用做通讯服务程序,通过Socket与IOT设备进行通讯。因为了解这个程序的使用场景,所以对于该程序的内存占用偏高产生了怀疑。该程序服务的设备并不多,但是占用了几百兆的内存,很明显是存在问题的。

对于该进程随后进行的分析也验证了这个想法,由于这个问题相对来说比较典型,因此比较具有分享价值,通过对于该案例的分享希望可以让更多人了解和掌握内存泄漏问题分析的一般方法。

内存泄漏问题分析的基本步骤

内存泄漏问题的分析可以分为三大部分:

确认问题

定位问题

解决问题

确认问题即确认内存确实存在泄漏问题,这个步骤不是光看看就可以,还需要尽量的保留问题发生的现场。不管是什么样的内存泄漏问题,最好能够保留内存镜像用于分析(dump文件),因为内存泄漏问题有时候是瞬间的,如果不及时保留现场,等到有时间看的时候,可能程序已经恢复正常。保存内存镜像文件的时候最好可以间隔一段时间保留多个镜像文件用于对比分析,可以更好的定位问题。

windbg的角度分析问题

通过windbg扩展项sos,分析dump文件中的句柄和内存里面的对象类型。sos随着.net framework一起安装,可以适用于大多数情况下的调试。

首先检查内存中的对象统计信息,输入!dumpheap -stat:

0:000> !dumpheap -stat Statistics: MT Count TotalSize Class Name …… 6c3ab8d4 806 38688 System.RuntimeMethodInfoStub 6c363e90 2592 39424 System.RuntimeType[] 6b68105c 2265 45300 System.Net.SafeCloseSocket+InnerSafeCloseSocket 6b680f2c 2265 45300 System.Net.SafeNativeOverlapped 6c36d120 476 45696 System.Reflection.Emit.DynamicILGenerator 08eb8b40 334 49432 Newtonsoft.Json.Serialization.JsonProperty 6b671564 2264 54336 System.Net.Sockets.OverlappedCache 6c3a1dd8 1284 87312 System.Reflection.RuntimeParameterInfo 6c3a1d90 2092 92048 System.Signature …… 6c3a17a8 7179 114864 System.Int64 00d8a37c 10717 900228 ********.NetCommunicator.SocketConnectionInfo 6b674f28 10741 988172 System.Net.Sockets.Socket 6c35da78 88000 1056000 System.Object 08eb08c0 17403 1113792 Newtonsoft.Json.Linq.JProperty 082188ac 10717 1457512 System.Collections.Concurrent.ConcurrentDictionary`2+Node[[System.String, mscorlib],[*******.RoadGate.API.Entity.MessagePacketModel, ***.RoadGate.API.Entity]][] 6c35e0e4 472 68400932 System.Char[] 6c35d6d8 126896 198551106 System.String 00af6ca0 34283 464007622 Free 6c361d04 25662 1037503288 System.Byte[]

获取到内存中对象的统计信息后重点关注堆栈中数量较多的类型,通过分析发现内存中有一万多个socket对象,还有一万多个放在ConcurrentDictionary中的业务自定义的实体类对象。由于当前分析的程序是通讯服务器,socket的合理值很容易通过分析dump时刻的业务量得到结果(在本案例中肯定是不合理的)。

经过咨询得知当前通讯服务的通讯对象远远达不到上万客户端的水平,因此很明显是socket相关的对象的处理出现了问题,出现了泄漏问题。对于.net程序来说,socket相关对象属于非托管资源,非托管资源的使用原则上必须显式地进行释放或关闭操作。

对于应用创建的大多数对象,可以依赖 .NET 垃圾回收器来进行内存管理。 但是,如果创建包含非托管资源的对象,则当你使用完非托管资源后,必须显式释放这些资源。 最常用的非托管资源类型是包装操作系统资源的对象,如文件、窗口、网络连接或数据库连接。 虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但无法了解如何发布并清理这些非托管资源。

虽然已经定位到通讯服务对于socket的处理不当,但是非托管资源到底是因为未能显示执行dispose方法导致的问题,还是说这些对象一直被引用而无法被回收?想要对于非托管资源的问题进行详细分析,可以使用!finalizequeue命令进行分析。该命令有三个可选参数:

-detail:显示需要清理的任何 SyncBlocks 的额外信息,以及有关等待清理的任何 RuntimeCallableWrappers (RCW) 的额外信息,这个选项也是默认值。

-allReady:选项显示所有准备终止的对象,无论它们已被垃圾回收标记成这样,还是将被下一个垃圾回收标记。 “准备终止”列表中的对象为不再为根的可终止对象。

-short:将输出限制为每个对象的地址,可以跟-allReady或者-detail一起使用。

首先输入!finalizequeue -allready检查有多少可以回收的对象:

0:000> !finalizequeue -allready 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 71 finalizable objects (190ce568->190ce684) generation 1 has 32 finalizable objects (190ce4e8->190ce568) generation 2 has 56598 finalizable objects (19097090->190ce4e8) Finalizable but not rooted: 976186dc 97619774 9761981c 97619844 9da920c8 9da920e0 9da9213c 9da92150 …… Ready for finalization 0 objects (190ce684->190ce684) Statistics for all finalizable objects that are no longer rooted: MT Count TotalSize Class Name 6c3711b8 1 16 System.Threading.Gen2GcCallback 6c36b328 2 24 System.Threading.TimerHolder 6b68c0fc 1 24 System.Net.Sockets.TcpClient 6b671fec 1 40 System.Net.Sockets.NetworkStream 6b68105c 8 160 System.Net.SafeCloseSocket+InnerSafeCloseSocket 6b6811a8 8 192 System.Net.SafeCloseSocket 6c35e2cc 4 208 System.Threading.Thread 67b788b8 4 208 System.Windows.Forms.Control+ThreadMethodEntry 00d8a37c 4 336 **************.NetCommunicator.SocketConnectionInfo 6b690e80 4 400 System.Net.Sockets.AcceptOverlappedAsyncResult 6b680f2c 20 400 System.Net.SafeNativeOverlapped 6c362a18 21 420 Microsoft.Win32.SafeHandles.SafeWaitHandle 6b671564 20 480 System.Net.Sockets.OverlappedCache 6b674f28 9 828 System.Net.Sockets.Socket 6c36207c 107 1284 System.WeakReference 6b671800 99 9900 System.Net.Sockets.OverlappedAsyncResult Total 313 objects

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzdwx.html