6月30日我们发布了异步化改造后的博客程序之后,出现了高内存、高CPU、高线程数的不理想情况

经过一周的追查,终于水落日出——引起不理想情况的根源是我们修改过的EnyimMemcached代码存在内存泄漏问题。

而造成内存泄漏的根源是我们没有对SocketAsyncEventArgs进行Dispose,实际情况是我们当时根本没注意到SocketAsyncEventArgs实现了IDispose接口,而这个小小的疏忽竟然折腾了我们一个星期。

存在内存泄漏问题的代码是这样写的:

a) 异步从Socket中读取数据:

public async Task<byte[]> ReadBytesAsync(int count)
{
var args = new SocketAsyncEventArgs();
args.SetBuffer(new byte[count], , count);
var awaitable = new SocketAwaitable(args);
await this.socket.ReceiveAsync(awaitable);
return args.Buffer;
}

b) 异步向Socket中写入数据:

public async Task WriteSync(IList<ArraySegment<byte>> buffers)
{
var args = new SocketAsyncEventArgs();
args.BufferList = buffers;
var awaitable = new SocketAwaitable(args);
await this.socket.SendAsync(awaitable);
}

解决内存泄漏问题的方法很简单,using+Buffer.BlockCopy,代码如下:

a) 改进后的异步从Socket中读取数据:

public async Task<byte[]> ReadBytesAsync(int count)
{
using (var args = new SocketAsyncEventArgs())
{
args.SetBuffer(new byte[count], , count);
var awaitable = new SocketAwaitable(args);
await this.socket.ReceiveAsync(awaitable);
var receivedBytes = new Byte[args.BytesTransferred];
Buffer.BlockCopy(args.Buffer, , receivedBytes, , args.BytesTransferred);
return receivedBytes;
}
}

b) 改进后的异步向Socket中写入数据:

public async Task WriteSync(IList<ArraySegment<byte>> buffers)
{
using (var args = new SocketAsyncEventArgs())
{
args.BufferList = buffers;
var awaitable = new SocketAwaitable(args);
await this.socket.SendAsync(awaitable);
}
}

改进后的代码已发布至github:https://github.com/cnblogs/EnyimMemcached

你也许会问我们是如何监测到内存泄漏情况的呢?

我们借助于两个工具:Windows任务管理器与性能监视器。

1. 通过任务管理器,我们观察到w3wp占用的内存会持续增长,当到达5G左右,在8核8G的阿里云虚拟机上CPU就开始做坐过山车,只有回收程序池(重启w3wp进程)才能恢复正常。

2. 通过性能监视器,我们监测了两个指标:

a) \.NET CLR Memory(w3wp)\# Bytes in all Heaps (针对托管内存)

b) \Process(w3wp)\Private Bytes (针对非托管内存)

观察到的情况见下图:

(绿色是Private Bytes)

Bytes in all Heaps与Private Bytes都会出现持续增长。

而对SocketAsyncEventArgs进行Dispose之后,性能监视器看到的\.NET CLR Memory\# Bytes in all Heaps变成了这样:

\Process\Private Bytes也与Bytes in all Heaps相映成辉:

一看到这样的图形,你应该和我们一样感觉到了GC在背后辛勤工作的身影。

走进异步世界:EnyimMemcached异步化改造引起的内存泄漏的更多相关文章

  1. 走进异步世界-犯傻也值得分享:ConfigureAwait(false)使用经验分享

    在上周解决“博客程序异步化改造之后遭遇的性能问题”的过程中,我们干了一件自以为很有成就感的事——在表现层(MVC与WebForms)将所有使用await的地方都加上了ConfigureAwait(fa ...

  2. Dubbo 2.7新特性之异步化改造

    这是why技术的第1篇原创文章 我与Dubbo的二三事 我是2016年毕业的,在我毕业之前,我在学校里面学到的框架都是SSH,即struts+spring+hibernate,是的你没有看错,在大学里 ...

  3. JS的异步世界

    前言 JS的异步由来已久,各种异步概念也早早堆在开发者面前.可现实代码中,仍然充斥了各种因异步顺序处理不当的bug,或因不好好思考,或因不了解真相.今天,就特来再次好好探索一番JS的异步世界. 01 ...

  4. 小丁带你走进git世界一-git简单配置

    小丁带你走进git世界一-git简单配置 1.github的简单配置 配置提交代码的信息,例如是谁提交的代码之类的. git config  –global user.name BattleHeaer ...

  5. [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序

    [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序 本节导读: 本节主要说明使用异步进行程序设计的优缺点及如何通过异步编程. 使用 ...

  6. arm驱动linux异步通知与异步IO【转】

    转自:http://blog.csdn.net/chinazhangzhong123/article/details/51638793 <[ arm驱动] linux异步通知与 异步IO> ...

  7. C#“同步调用”、“异步调用”、“异步回调”

    本文将主要通过“同步调用”.“异步调用”.“异步回调”三个示例来讲解在用委托执行同一个“加法类”的时候的的区别和利弊. 首先,通过代码定义一个委托和下面三个示例将要调用的方法: ); //模拟该方法运 ...

  8. Linux设备驱动中的异步通知与异步I/O

    异步通知概念: 异步通知的意识是,一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上的“中断”概念,比较准确的称谓是“信号驱动的异步IO”,信号是在软件层次 ...

  9. Oracle 12C 新特性之表分区带 异步全局索引异步维护(一次add、truncate、drop、spilt、merge多个分区)

    实验准备:-- 创建实验表CREATE TABLE p_andy(ID number(10), NAME varchar2(40))PARTITION BY RANGE (id)(PARTITION ...

随机推荐

  1. zk系列-zookeeper的使用

    zk支持java/c访问,java常用的有apache-zkclient.社区版的i0tec-zkclient.github.adyliu,apache-zkclient是zk自身提供的接口,i0te ...

  2. C++ inline函数

    本文主要记录了C++中的inline函数,也就是内联函数,主要记录了以下几个问题: C++为什么引入inline函数? 为什么inline能很好的取代表达式形式的预定义? inline函数的使用场合 ...

  3. springSide部署出现AnnotationConfigUtils.processCommonDefinitionAnnotations(…) is not public!

    AnnotationConfigUtils.processCommonDefinitionAnnotations(…) is not public! Make sure you're using Sp ...

  4. 如何在 ETL 项目中统一管理上百个 SSIS 包的日志和包配置框架

    一直准备写这么一篇有关 SSIS 日志系统的文章,但是发现很难一次写的很完整.因为这篇文章的内容可扩展的性太强,每多扩展一部分就意味着需要更多代码,示例和理论支撑.因此,我选择我觉得比较通用的 LOG ...

  5. KendoUI系列:Grid

    1.基本使用 <div id="grid"></div> <link href="@Url.Content("~/Content/ ...

  6. base64:URL背景图片与web页面性能优化

    一.base64百科 Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,可用于在HTTP环境下传递较长的标识信息. 某人: 唉,我彻底废柴了,为何上面明明是中文,洒家却看不懂嘞,为什 ...

  7. JavaScript与有限状态机

    有限状态机(Finite-state machine)是一个非常有用的模型,可以模拟世界上大部分事物. 简单说,它有三个特征: * 状态总数(state)是有限的. * 任一时刻,只处在一种状态之中. ...

  8. 利用跨域请求来隐藏firbug控制台中的Ajax请求

    本文链接:http://www.orlion.ml/63/ 普通jquery的Ajax请求在控制台中是可见的,如下: 而在利用jsonp(json with padding)进行Ajax跨域时,发现A ...

  9. java 模拟qq源码

    java 模拟qq源码: http://files.cnblogs.com/files/hujunzheng/QQ--hjzgg.zip

  10. Docker之Linux Cgroups

    Linux Cgroups介绍 上面是构建Linux容器的namespace技术,它帮进程隔离出自己单独的空间,但Docker又是怎么限制每个空间的大小,保证他们不会互相争抢呢?那么就要用到Linux ...