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异常解析相关推荐
- 深入JVM——OOM异常解析
转载自 深入JVM--OOM异常解析 JVM对象访问解析 对象访问过程的内存情况 public void function(){Object obj = new Object(); } ? func ...
- 事务传播机制/数据库异常解析——2016-8-13分享总结
一. 事务的传播机制/required 跟 required new 的使用与区别 基础回顾 1.1 事务的隔离级别: ISOLATION_READ_UNCOMMITTED(读未提交) ISOLATI ...
- 爬虫实战学习笔记_3 网络请求urllib模块:设置IP代理+处理请求异常+解析URL+解码+编码+组合URL+URL连接
1 设置IP代理 1.1 方法论述 使用urllib模块设置代理IP是比较简单的,首先需要创建ProxyHandler对象,其参数为字典类型的代理IP,键名为协议类型(如HTTP或者HTTPS),值为 ...
- InvocationTargetException异常解析
InvocationTargetException异常由Method.invoke(obj, args...)方法抛出.当被调用的方法的内部抛出了异常而没有被捕获时,将由此异常接收. 示例: [jav ...
- 异常解析————Parameter metadata not available for the given statement
引言 在将数据存入mysql数据库时抛出异常:Parameter metadata not available for the given statement.参数元数据对于给定的声明不可用. SQL ...
- java的异常解析_java异常解析 - liop的个人空间 - OSCHINA - 中文开源技术交流社区...
抛出异常,捕捉异常,输出异常. /** * 自定义异常类 */ public class CustomerException extends RuntimeException { private St ...
- NoSuchMethodError异常解析
NoSuchMethodError是一个运行时错误,在编译时一般不会出现这个错误. 既然能成功编译,就说明方法本身是存在的,方法所在的类也是存在的,而且都可以正常的引用到. 那么为什么还会出现这个错误 ...
- Java基础学习总结(129)——Arrays.asList得到的List进行add和remove等操作出现异常解析
将一个Array的对象转化为List.常常使用Arrays.asList()这个方法,如下单元测试案例: @Testpublic void testArraysAsList() {List<In ...
- 那些年,我们解析过的前端异常
前言 本文目的: 能让非前端同学大致了解下,现代『前端异常解析』是怎么做的,以及大部分的坑会是哪些 对于专业的前端同学,本文中也许有些坑,你还没有踩到,也可以看下. 从 window.onerror ...
最新文章
- 第五节 RabbitMQ在C#端的应用-消息收发
- python软件下载免费还是收费-为什么python最强大的IDE是收费的PyCharm?
- Linux-鸟菜-6-文件与目录管理
- OleDbCommand使用参数应该注意的地方
- 主题:Centos6.4安装JDK
- SAP CRM和C4C数据同步的两种方式概述:SAP PI和HCI
- php 谷歌翻译api_武汉武昌区地质勘测翻译公司-译嘉合翻译
- vue-router传参的坑(query和params)
- 感性精品高清PSD美手分层海报,一键替换,奢华品、首饰、护肤品推荐临摹应用
- 为什么坚持一件事总是那么难,而且有时候总是三分钟热度?
- 将Docker image push 到azure
- Iconfont-阿里巴巴矢量图标库
- pta求阶乘序列前n项和_求极限方法总结
- 计算机管理系统权限申请审批表,开通权限申请书范文
- Desktoppr与 Dropbox国内成功使用
- 利用WinPcap模拟网络包伪造飞秋闪屏报文
- 【ubuntu】virtualbox安装增强功能时:未能加载虚拟光盘到虚拟电脑
- 2种方式获取StreamingAssets下音频
- QQ邮箱设置企业邮箱别名邮箱
- 收费变免费,是商业模式的颠覆式创新