一、概要

大概在今年三月份的时候突然被紧急调到另外一个项目组解决线上内存泄漏问题。经过两周的玩命奋战终于解决了这个问题这里把心路历程及思路分享给大家。希望可以帮助到各位或现在正遇到这样事情的小伙伴提供一些思路。

二、场景

当部门老大找到我的时候,给我描述了这样一段话。

“目前服务出现了提交内存泄漏的问题,目前分析出来可能是日志组件有大量的日志消息堆积把内存占满导致服务崩溃了。在国内某地区客户的服务器上15000台物联网设备不能正常工作这个问题非常紧急需要马上解决。”

问题描述至此,没有其他可用信息。这时候我先崩溃了...但是任务找到你不能说不行。万一解决了这种重大事故还能在部门老大面前秀一把。

三、思路

(1)分析

Part1,分析日志堆积原因

  1. 拿到服务器地址去翻出日志文件,查看日志内容;内容基本上都是一些报错情况xxx对象为null,对象转换失败。
  2. 日志组件的实现也比较糟糕Log对象在每个调用的类里都会重新new

解决方案:

  1. 修复对象为null的问题并加上空值判断,大概的原因就是json值转换的时候传入的值是null那么就引起这两块的连锁反应。非常值得注意的一点是通常json对象转换的地方都会加入try块去捕获异常在程序里try的捕捉是会对.net程序造成性能影响的所以能用判断规避的尽量不要去触发try机制,程序性能被拖下去其他方面的处理就会变相的削减处理速度变慢那么数据堆积好像就解释的通了。
  2. 将日志组件重构为单例且线程安全的实现,写入日志的数据结构体是class这里改成struct,考虑的因素是引用类型会存在引用问题再就是考虑的值类型和引用类型在内存中占用的大小是不一样的,而且值类型和引用类型在处理速度上值类型更快。

以为这样就结束了吗?不,当程序改好之后放在测试服务器上跑第二天早上测试部的小姐姐就找到我说异常报错情况是好了,但是内存泄漏还是没解决。

Part2,查找内存泄漏的根本原因

看来Part1的操作仅仅只是修复了一个小bug而已,并不是我所想的那么简单,在日志的查看中还发现log日志中出现“tcp服务拒绝连接XXX异常”。当我看到这些的时候心情糟糕透了....

1.一早我就用Profile把服务程序跑了一遍发现了

  • (1)有几个消息队列占用非常大,查阅代码之后发现服务端程序会和15000台物联网设备进行交互的所有数据都会先堆积到这个队列里如果这个队列满了(Queue上限被设定2w)会new新的Queue然后把溢出的部分转到新的Queue里,最可怕的是从队列里取数据的还是单线程处理。

  • (2)还会有很多磁盘I/O的操作会存储在应用服务器本机上例如socket通讯的报文和需要转发的内容等等都会进行写入操作。
  • (3)逐步调试的时候发现大部分的方法实现都是同步方法,而且框架版本居然是.net freamwork4。

解决方案:

(1)

  • 【移除new新队列的机制、删除Main Queue的上限设置改为多线程处理Queue;一切数据堆积的本质就是数据处理不过来所以开辟再多的内存空间都是慢性死亡而已。】
  • 【走访物联网硬件部门,询问物联网设备发送数据频率、设备数、单台设备发送单条数据的大小是多少KB;为什么需要了解?这些第一点在程序内记录日志然后统计成走势图能直接观察队列内部的变化开会的时候能给领导具有说服力的证据能看到数据量什么时候陡增、数据大小等;第二点因为这些报文数据需要存在应用服务器本地那么这时候就能计算出写入的数据量有没有超出普通硬盘的写入I/O瓶颈以及网络带宽的占用。】
  • 【走访物联网硬件部门2,询问物联网设备socket传输数据时是否有走正常“tcp挥手”流程;为什么?因为socket tcp通讯中,是双工通道那么其中有一端突然断开,另一端会进入“wait”状态不会及时回收tcp连接资源,大家试想一下如果15000台设备高频短连接去操作那么服务端连接队列资源很有可能吃不消。这个时候就需要服务端主动断开“失效”连接及时回收资源“拆除双工通道”以及调整socket连接队列大小。】

(2)磁盘写入报文信息这块,就要用三寸不烂之舌说动项目经理把这块砍掉以节约CPU性能以及减少磁盘I/O,大伙试想一下每次socket通讯进行收发的时候都要去操作一下I/O那是多么恐怖的一件事情;最后沟通结果那个组的项目经理同意砍掉部分模块磁盘写入功能,那么问题来了剩下的怎么办如何将优势进一步扩大?这时候继续查阅项目代码,结果发现socket通讯中“收”、“发”都会操作一次。那么这时候需要做的是将报文积累到一定数量比如说积累1000条报文再一次性写入那么磁盘I/O的操作频率将成倍递减。

(3)最后一个问题,就是讲所有的方法修改为异步方法。这时候就能祭出Task、Async、Await了。但是基于的框架是.net freamwork4的,后来又去查阅MSDN的文档发现.net freamwork4远古框架中还是有这些特性的虽然用法稍微难受点但是还是能优化的。一定要记住一点,开发服务端要有“服务端”思维如果都是同步方法就会被同步阻塞处于“等待处理结果状态”这样的话服务端的并发量是上不去的。

这里虽然没怎么用上的一发大招,但是这里还是分享给大家“注释大法”;注释掉最有可能出问题的地方逐一排查一定能发现问题的所在就是非常的耗时那会我基本每天工作12小时,尤其是公司的远古项目通常“代码烂”、“设计基本没有”、“使用的.net框架版本低”等等,一堆恶心人的事情发生。

(2)工具

  • Visual Studio自带的Profile。【可以分析CPU、内存等占用情况;这款比较推荐】
  • VMMap【可以分析CPU、内存等占用情况】
  • ANTS Performance Profiler【这款工具比较强大能分析调用链路逐级告诉你内存占用的地方以及内存占用大小】
  • Window操作系统自带的资源监视器这个不用多说大家都会用。

Part3,总结

基于以上的修改,在测试服务器上稳定运行3周内存稳定在2.9G左右;

一定要记住:

  • “遇到任何棘手的事情不要抱怨。”
  • “一个优秀的软件工程招聘进来就是解决问题的,而不是制造问题;”
  • “对于任务的安排,高手永远都是说出解决问题的期限;到点交东西。而不是支支吾吾说不清楚、退缩。”
  • “遇到问题冷静思考,相信自己一定可以的;那怕失败去尝试一下也好。”
  • “没解决问题的时候不要说任何话,说什么都像是在找理由。闭上嘴巴去想办法。”

其实解决这个问题时期发生了很多有趣的故事,不过最终还是要解决难啃的问题证明自己,开发学习本身就是一个不断变强的过程“修技术,也修内心”当自己逐渐变强之后也不要鄙视技术不好的同事始终保持一颗学徒的心。

Part4,彩蛋

解决这个问题之后在同部门同事的眼里威望都会有提升(尤其是测试部门的小姐姐,因为她们不用费力的每天去看服务器了),最终解决项目的重大事故部门老大给了机会调到其他省的研发中心当项目经理薪资平移的基础上再上浮百分之十。可见掌握一手救急的技能有多么划算。

.Net程序内存泄漏解析的更多相关文章

  1. C/C++应用程序内存泄漏检查统计方案

    一.前绪 C/C++程序给某些程序员的几大印象之一就是内存自己管理容易泄漏容易崩,笔者曾经在一个产品中使用C语言开发维护部分模块,只要产品有内存泄漏和崩溃的问题,就被甩锅“我的程序是C#开发的内存都是 ...

  2. Java虚拟机性能管理神器 - VisualVM(6) 排查JAVA应用程序内存泄漏【转】

    Java虚拟机性能管理神器 - VisualVM(6) 排查JAVA应用程序内存泄漏[转] 标签: javajvm内存泄漏监控工具 2015-03-11 18:30 1870人阅读 评论(0) 收藏  ...

  3. C++程序内存泄漏检测方法

    一.前言 在Linux平台上有valgrind可以非常方便的帮助我们定位内存泄漏,因为Linux在开发领域的使用场景大多是跑服务器,再加上它的开源属性,相对而言,处理问题容易形成“统一”的标准.而在W ...

  4. windbg分析net程序内存泄漏问题

    1       问题简介 有客户反馈,打了最新补丁后,服务器的内存暴涨,一直降不下来,程序非常卡.在客户的服务器上抓了一个dump文件,开始分析. 分析问题的思路: 1.找到是那些资源占用了大量内存? ...

  5. 【转】Unix下C程序内存泄漏检测工具Valgrind安装与使用

    Valgrind是一款用于内存调试.内存泄漏检测以及性能分析的软件开发工具. Valgrind的最初作者是Julian Seward,他于2006年由于在开发Valgrind上的工作获得了第二届Goo ...

  6. Java内存泄漏解析!

    前言: 内存管理是Java最重要的优势之一,你只需创建对象,Java垃圾收集器会自动负责分配和释放内存.但是,情况并不那么简单,因为在Java应用程序中经常发生内存泄漏. 本章会说明什么是内存泄漏,为 ...

  7. WPF应用程序内存泄漏的一些原因

    原文:Finding Memory Leaks in WPF-based applications There are numbers of blogs that folks wrote about ...

  8. 【知识必备】内存泄漏全解析,从此拒绝ANR,让OOM远离你的身边,跟内存泄漏say byebye

    一.写在前面 对于C++来说,内存泄漏就是new出来的对象没有delete,俗称野指针:而对于java来说,就是new出来的Object放在Heap上无法被GC回收:而这里就把我之前的一篇内存泄漏的总 ...

  9. 使用WinDug工具调试c#程序或c++程序的dmp崩溃文件,调试内存泄漏

    1.调试c#程序内存泄漏步骤 设置symbol符号路径: SRV*c:\mysymbol* http://msdl.microsoft.com/download/symbols;d:/你的pdb文件路 ...

随机推荐

  1. 用Python实现一个“百度翻译”

    import requests import json s = input("请输入你要翻译的内容:") headers = {"User-Agent":&qu ...

  2. net字符串倒置和冒泡排序

    using System;using System.Configuration;using System.Data;using System.Linq;using System.Web;using S ...

  3. Java基本概念:继承

    一.简介 描述: 现实世界中的继承无处不在.比如:动物细分有哺乳动物.爬行动物等,哺乳动物细分有灵长目.鲸目等. 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模. 继承是类和类之间的一种关 ...

  4. 第39天学习打卡(多线程 Thread Runnable 初始并发问题 Callable )

    多线程详解 01线程简介 Process与Thread 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念. 进程则是执行程序的一次执行过程,它是一个动态的概念.是系统资源分配的 ...

  5. Linux没有ens33解决方案

    一.前言 运行环境:window10+VMware14+Centos7 博主最近遇到一个比较郁闷的问题,在虚拟机上操作Linux系统查看IP的时候,发现没有ens33或者eth0了,试了很多办法都没有 ...

  6. alpine jdk 中文乱码

    一.概述 使用alpine镜像构建了一个oracle jdk的镜像,运行java业务时,查看日志,显示中文乱码. 但是,基于Alpine Linux的Docker基础镜像的镜像文件很小,也有代价: 把 ...

  7. JPEG解码——(5)反量化和逆ZigZag变换

    本篇是该系列的第五篇,承接上篇huffman解码,介绍接下来的两个步骤--反量化和逆zigzag变换,即IDCT前的两个步骤. 需要说明的是,这两个步骤可以颠倒,本人的实现是,先反量化,再逆ZigZa ...

  8. 分布式文件系统FastDFS简介、搭建、与SpringBoot整合实现图片上传

    之前大学时搭建过一个FastDFS的图片服务器,当时只是抱着好奇的态度搭着玩一下,当时搭建采用了一台虚拟机,tracker和storage服务在一台机器上放着,最近翻之前的博客突然想着在两台机器上搭建 ...

  9. LeetCode-[list-of-depth-lcci]

    特定深度节点链表-求解每一层二叉树从左到右遍历形成的链表 list-of-depth-lcci 这是关于二叉树的问题,遍历每一层的结点并且存在链表中. 可以采取队列类似于广度优先搜索的方法进行搜索.每 ...

  10. scrapy框架的介绍与安装

    scrapy框架的原理 使用pycharm安装scrapy库 1.打开新建file,然后有个扳手的setings点击进去,如图所示: 2.选择project 然后点击python interprete ...