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

在某次巡查生产环境监控数据的时候,发现某个程序的内存占用偏高(大于1G) 。这个程序是用做通讯服务程序,通过Socket与IoT设备进行通讯 。因为这个程序接入的设备并不多,所以对于该程序的内存占用偏高产生了怀疑 。该程序占用了几百兆的内存,很明显是存在问题的 。
对于该进程随后进行的分析也验证了这个想法,由于这个问题相对来说比较典型,因此比较具有分享价值,通过对于该案例的分享可以让更多人了解和掌握内存泄漏问题分析的一般方法 。
内存泄漏问题分析的基本步骤内存泄漏问题的分析可以分为三阶段:

  1. 确认问题
  2. 定位问题
  3. 解决问题
确认问题即确认内存确实存在泄漏问题,这个步骤不是光看看就可以,还需要尽量的保留问题发生的现场 。不管是什么样的内存泄漏问题,最好能够保留内存镜像用于分析(dump文件),因为内存泄漏问题有时候是瞬间的,如果不及时保留现场,等到有时间看的时候,可能程序已经恢复正常 。保存内存镜像文件的时候最好可以间隔一段时间保留多个镜像文件用于对比分析,可以更好的定位问题 。
从windbg的角度分析问题通过windbg加载SOS插件,分析dump文件中的句柄和内存里面的对象类型 。SOS随着.Net Framework一起安装,可以适用于大多数情况下的调试 。
首先检查内存中的对象统计信息,输入 !dumpheap-stat
  1. 0:000> !dumpheap -stat
  2. Statistics:
  3. 【内存泄漏问题分析之非托管资源泄漏】MTCountTotalSize Class Name
  4. ……
  5. 6c3ab8d480638688 System.RuntimeMethodInfoStub
  6. 6c363e90259239424 System.RuntimeType[]
  7. 6b68105c226545300 System.Net.SafeCloseSocket+InnerSafeCloseSocket
  8. 6b680f2c226545300 System.Net.SafeNativeOverlapped
  9. 6c36d12047645696 System.Reflection.Emit.DynamicILGenerator
  10. 08eb8b4033449432 Newtonsoft.Json.Serialization.JsonProperty
  11. 6b671564226454336 System.Net.Sockets.OverlappedCache
  12. 6c3a1dd8128487312 System.Reflection.RuntimeParameterInfo
  13. 6c3a1d90209292048 System.Signature
  14. ……
  15. 6c3a17a87179114864 System.Int64
  16. 00d8a37c10717900228 ********.NetCommunicator.SocketConnectionInfo
  17. 6b674f2810741988172 System.Net.Sockets.Socket
  18. 6c35da78880001056000 System.Object
  19. 08eb08c0174031113792 Newtonsoft.Json.Linq.JProperty
  20. 082188ac107171457512 System.Collections.Concurrent.ConcurrentDictionary`2+Node[[System.String, mscorlib],[*******.RoadGate.API.Entity.MessagePacketModel, ***.RoadGate.API.Entity]][]
  21. 6c35e0e447268400932 System.Char[]
  22. 6c35d6d8126896198551106 System.String
  23. 00af6ca034283464007622Free
  24. 6c361d04256621037503288 System.Byte[]
获取到内存中对象的统计信息后重点关注堆栈中数量较多的类型,通过分析发现内存中有一万多个socket对象,还有一万多个放在ConcurrentDictionary中的业务自定义的实体类对象 。由于当前分析的程序是通讯服务器,socket的合理值很容易通过分析dump时刻的业务量得到结果(在本案例中肯定是不合理的) 。
经过咨询得知当前通讯服务的通讯对象远远达不到上万客户端的水平,因此很明显是socket相关的对象的处理出现了问题,出现了泄漏问题 。对于.net程序来说,socket相关对象属于非托管资源,非托管资源的使用原则上必须显式地进行释放或关闭操作 。

推荐阅读