OutOfMemoryException异常解析
一、概述
在国庆休假快结束的最后一天晚上接到了部门老大的电话,某省的服务会出现崩溃问题。需要赶紧修复,没错这次的主角依旧是上次的“远古项目”没有办法同事都在休假没有人能帮忙开电脑远程只能打车去公司。远程链接上服务器之后查看日志发现抛出的堆栈异常信息中包含了这样一句话“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异常解析的更多相关文章
- CSharpFlink分布式实时计算,OutOfMemoryException异常,你意想不到的原因。
目录 一.测试过程及问题 二.问题排查及分析过程 三.问题分析及解决过程 四.问题解决初步结果 一.测试过程及问题 从昨天15点左右开始测试,1个主节点,10个计算节点,1000个数据点,每个数据点3 ...
- SSM-SpringMVC-23:SpringMVC中初探异常解析器
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客要讲的是异常解析器,SimpleMappingExceptionResolver简单映射异常解析器 可 ...
- SpringMVC源码阅读:异常解析器
1.前言 SpringMVC是目前J2EE平台的主流Web框架,不熟悉的园友可以看SpringMVC源码阅读入门,它交代了SpringMVC的基础知识和源码阅读的技巧 本文将通过源码(基于Spring ...
- 遇到OutOfMemoryException异常了
遇到OutOfMemoryException异常了 2008-11-28 09:52 asp.net做的售后服务系统运行了快1年了,昨天在做全年数据导出的时候出现OutOfMemoryExceptio ...
- (转载)activity外部调用startActivity的new task异常解析
activity外部调用startActivity的new task异常解析 泡在网上的日子 / 文 发表于2013-09-07 12:45 第1314次阅读 异常,android,activity ...
- SpringMVC框架中的异常解析器-ExceptionHandler和HandlerExceptionResolver
SpringMVC框架中,处理异常还是挺方便的,提供了一个异常解析器. 处理局部异常 @Controller public class AccessController { /** * 处理这个Con ...
- Java错误和异常解析
Java错误和异常解析 错误和异常 在Java中, 根据错误性质将运行错误分为两类: 错误和异常. 在Java程序的执行过程中, 如果出现了异常事件, 就会生成一个异常对象. 生成的异常对象将传递Ja ...
- springmvc<三> 异常解析链与视图解析链
1.1.7. Exceptions - 如果异常被Controller抛出,则DispatchServlet委托异常解析链来处理异常并提供处理方案(通常是一个错误的响应) spri ...
- JAVA常见异常解析
1. java.lang.nullpointerexception 这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对 ...
随机推荐
- Python中的多线程编程
前言: 线程是操作系统能够进行运算调度的最小单位(程序执行流的最小单元) 它被包含在进程之中,是进程中的实际运作单位 一个进程中可以并发多个线程每条线程并行执行不同的任务 (线程是进程中的一个实体,是 ...
- vue 导出excel后端返回乱码下载不了的解析问题
有俩种方法可以用 第一种方法: 解析:直接解析并且下载后端的乱码 this.download('后端给的导出excel的方法', { ...this.queryParams }, ` ...
- 根据短链生成二维码并上传七牛云(Java)
通过短链生成二维码并上传七牛云(Java) 前言 网上这种帖子其实也是很多,大部分搜出来的是CSDN的,然后点进去一看都几乎一样:所以这次给个自己实践的例子记录. 这次也是通过搜索得到的一部分能实现这 ...
- Shiro02
Shiro认证 Pom依赖 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>s ...
- Django的form组件基本使用——生成标签
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = ...
- C语言实现任务调度与定时器
代码实现是在xl2tpd的源码中get到的,感觉很有意思的一段代码.基本功能就是实现定时器,时间到后从定时队列中取出,然后完成指定的任务. 1. schedule.c代码(自己添加了main函数,用来 ...
- Request请求对象
一.Request对象由服务器创建,我们使用 浏览器访问服务器资源原理: 二.Request体系结构 其中,servlet 的service()方法参数列表是 servletRequest对象, Ht ...
- Centos下Yum安装PHP7.0
默认的版本太低了,手动安装有一些麻烦,想采用Yum安装的可以使用下面的方案: 1.检查当前安装的PHP包 yum list installed | grep php 如果有安装的PHP包,先删除他们 ...
- 安装docker遇到:package docker-ce-3:19.03.8-3.el7.x86_64 requires containerd.io >= 1.2.2-3, but none of the providers can be installed
执行 yum install docker-ce docker-ce-cli containerd.io 提示: 错误: 问题: package docker-ce-3:19.03.8-3.el7.x ...
- dedecms织梦修改标题默认长度
1 先在后台管理: 2 在数据库修改表dede_archives: ALTER TABLE `dede_archives` CHANGE `title` `title` VARCHAR( 250 ) ...