一、概述

在国庆休假快结束的最后一天晚上接到了部门老大的电话,某省的服务会出现崩溃问题。需要赶紧修复,没错这次的主角依旧是上次的“远古项目”没有办法同事都在休假没有人能帮忙开电脑远程只能打车去公司。远程链接上服务器之后查看日志发现抛出的堆栈异常信息中包含了这样一句话“OutOfMemoryException”,在A.dll中。

这个“远古项目”大致情况如下:

  • 1.框架版本为.net framework 4
  • 2.代码结构混乱
  • 3.需要通过socket连接大量的物理设备采集数据(1000台左右)
  • 4.每小时采集一次,并由“远古项目”接收并转发

二、问题分析

(1)根据日志定位问题

其实日志中能给定的错误信息有限,但是也有很大帮助起码知道问题出在哪一块。这时候直接找到A类库查看源码,这时候发现项目当中这一块代码非常多大约1000行左右,这么多代码到底哪一句出了问题不得而知。同时如果我想复现的话并不能有那么多的设备去模拟测试。这时候其实是有点晕的,这时候只能硬着头皮把“OutOfMemoryException”这个异常拿去google一样,结果发现是线程方面的内存溢出问题。那么这时候缩小了查看代码的范围,就开始在代码中搜索Thread对象的使用。查看了半天果然有发现,看到了如下一段代码:

//...代码上下文
byte[] sendbytes = new byte[] { xxx };
var thread = new Thread((_) =>
{
Send(sendbytes);
});
thread.Start();
//...代码上下文

这段代码存在的地方大概是,在所有设备在每小时采集一次数据的时候会集中在某一个时间段里大量的发送数据若干次,且每台设备每次发送数据的时候都会创建线程去发送数据。看到这一段代码的时候我人都麻了。

(2)根据问题代码继续分析

在程序开发中,创建线程的代价是非常高昂的。而且都集中在一个时间点上去频繁创建线程这样的代码肯定不行。这段代码极有可能就是引发这个异常的原因之一。分析到这里突然想起之前看过的一本书,书中描述了这样一段话:

“线程栈往往都很小。windows上默认情况下栈最大为1MB,并且大多数线程通常只使用很少的栈页(stack page)。….”(书名:.NET性能分析)

基于以上理论增加了那段代码引起崩溃的可能性怀疑。那么只能抱着试一试的心态继续往下做。

三、解决方案

那么发现了可能导致异常的代码如何去解决呢?这时候又有点头疼了,因为我暂时能想到的解是:

Answer:利用生产消费者模式建立发送队列,然后开启一个常驻的发送线程慢慢发就可以了。

但是问题来了,“远古项目”中结构太过混乱牵一发动全身的连最简单的这种思路实现都会有很多阻碍。而且框架版本过于低一些新语法特性也不能使用。

这个时候在想如果想解决这个问题应该要死扣住以下几点:

  • 1.不能频繁创建线程
  • 2.不能对代码有过大的改动
  • 3.对线程创建以及数量要有良好的控制
  • 4.不能考虑使用新语法特性

ThreadPool这个对象不是刚好满足这个情况吗,这时候将代码修改为:

byte[] sendbytes = new byte[] { 0 };
ThreadPool.QueueUserWorkItem(_=>
{
Send(sendbytes);
});

其实实现这一段代码之后,心里依旧没有解决问题的喜悦。因为根据线程池的原理,如果任务量过大的话还是会开辟默认线程数量以外的新线程。但是线程池对线程管理比较好,这样的该的结果就能直接提交代码重新去服务器上部署吗?心里还是没有底我又做了如下测试:

//模拟在同一个时间点内大量开启线程模拟多设备发送数据
for (int i = 0; i < 10000; i++)
{
var thread = new Thread((_) =>
{
//模拟发送数据耗时
Thread.Sleep(1000);
});
thread.Start();
}

观察VS内存监测变化。

发现在大量创建线程的时候CPU和内存占用会陡增。那么接下来我在试一试用线程池去执行这些操作会是一个什么情况代码修改如下:

for (int i = 0; i < 100000; i++)
{
ThreadPool.QueueUserWorkItem(_=>
{
Thread.Sleep(1000);
});
}

继续观察VS内存监测变化。

发现几乎没有任何波澜,看到这个测试结果只能说感谢微软实现了一个如此优秀的线程池。这个时候由于时间紧迫只能先改一版本拿到服务器上去顶一阵肯定比上一个版本要好。

问题又来了如果再继续出现问题怎么继续排查?下一次不一定能抛出更有用的信息。这个时候想到的解决方案如下:

  • 1.添加DUMP文件输出
  • 2.关键敏感地方加强日志信息详细程度和适量try块捕获异常

到此耗时大约3小时左右,编译好版本部署到服务器上再做观察。就这样观察了一个多星期没有再次出现崩溃异常。其实分析下来,发现对这个问题发生原理可能还没有玩明白需要继续研究。

OutOfMemoryException异常解析的更多相关文章

  1. CSharpFlink分布式实时计算,OutOfMemoryException异常,你意想不到的原因。

    目录 一.测试过程及问题 二.问题排查及分析过程 三.问题分析及解决过程 四.问题解决初步结果 一.测试过程及问题 从昨天15点左右开始测试,1个主节点,10个计算节点,1000个数据点,每个数据点3 ...

  2. SSM-SpringMVC-23:SpringMVC中初探异常解析器

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客要讲的是异常解析器,SimpleMappingExceptionResolver简单映射异常解析器 可 ...

  3. SpringMVC源码阅读:异常解析器

    1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...

  4. 遇到OutOfMemoryException异常了

    遇到OutOfMemoryException异常了 2008-11-28 09:52 asp.net做的售后服务系统运行了快1年了,昨天在做全年数据导出的时候出现OutOfMemoryExceptio ...

  5. (转载)activity外部调用startActivity的new task异常解析

    activity外部调用startActivity的new task异常解析 泡在网上的日子 / 文 发表于2013-09-07 12:45  第1314次阅读 异常,android,activity ...

  6. SpringMVC框架中的异常解析器-ExceptionHandler和HandlerExceptionResolver

    SpringMVC框架中,处理异常还是挺方便的,提供了一个异常解析器. 处理局部异常 @Controller public class AccessController { /** * 处理这个Con ...

  7. Java错误和异常解析

    Java错误和异常解析 错误和异常 在Java中, 根据错误性质将运行错误分为两类: 错误和异常. 在Java程序的执行过程中, 如果出现了异常事件, 就会生成一个异常对象. 生成的异常对象将传递Ja ...

  8. springmvc<三> 异常解析链与视图解析链

    1.1.7. Exceptions    - 如果异常被Controller抛出,则DispatchServlet委托异常解析链来处理异常并提供处理方案(通常是一个错误的响应)        spri ...

  9. JAVA常见异常解析

    1. java.lang.nullpointerexception 这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对 ...

随机推荐

  1. Mysql索引最佳实践笔记0524

    #mysql5.7 innodb默认存储引擎 一.关于索引二.最佳实践三.避坑实践 一.关于索引 1.索引的作用 -提高查询效率 -数据分组.排序 -避免回表查询 -优化聚集查询 -用于多表join关 ...

  2. Three.js 中 相机的常用参数含义

    Three.js 中相机常用的参数有up.position和lookAt. position是指相机所在的位置,将人头比作相机的话,那么position就是人头的中心的位置: up类似于人的脖子可以调 ...

  3. i++ ++i 理解

    i++与++i怎么运算,解决办法: i++规则是在表达式中先取i的值使用,后让i的值变化成加1后的值. 举例:如在式中 j=i++,他就会变成这样的两步,第一步先执行j=i,第二步执行i=i+1. + ...

  4. Java 学习:数据类型

    前言:Java属于强类型语言 强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用 优势就是安全性高,但劣势速度慢 数据类型 Java的数据类型分为两大类: 基本类型(primit ...

  5. 一、Rabbitmq的简单介绍

    以下只是本人从零学习过程的整理 部分内容参考地址:https://www.cnblogs.com/ysocean/p/9240877.html 1.RabbitMQ的概念 RabbitMQ是实现了高级 ...

  6. Java调用腾讯云短信接口,完成验证码的发送(不成功你来砍我!!)

    一.前言 我们在一些网站注册页面,经常会见到手机验证码的存在,这些验证码一般的小公司都是去买一些大的厂家的短信服务,自己开发对小公司的成本花费太大了!今天小编就带着大家来学习一下腾讯云的短信接口,体验 ...

  7. Pytest系列(15)- 多重校验插件之pytest-assume的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 pytest中可以用pyth ...

  8. openswan协商流程之(六):main_inI3_outR3()

    主模式第六包:main_inI3_outR3 1. 序言 main_inI3_outR3()函数是ISAKMP协商过程中第六包的核心处理函数的入口,第五六包主要用来验证对方的身份信息,同时此报文也是加 ...

  9. liquibase新增字段注释导致表格注释同时变更bug记录

    liquibase是一个用于数据库变更跟踪.版本管理和自动部署的开源工具.它的使用方式方法可以参考官方文档或者其他人的博客,这里不做过多介绍. 1. 问题复现 在使用过程中发现了一个版本bug.这个b ...

  10. RocketMQ详解(四)核心设计原理

    专题目录 RocketMQ详解(一)原理概览 RocketMQ详解(二)安装使用详解 RocketMQ详解(三)启动运行原理 RocketMQ详解(四)核心设计原理 RocketMQ详解(五)总结提高 ...