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. ejs模板中的四种表达式输出形式

    在ejs模板中,通常会用下面四种方式在HTML中输出服务端的变量或表达式的值: 1. 直接在<%%>中写表达式或变量.这种情况通常只是用来进行表达式计算或给变量赋值,不会有任何输出,被称作 ...

  2. 开启Ubuntu root 远程登录

    很早就遇到这问题了,但是今天才想到解决.也就是说Ubuntu在安装的时候,远程SSH登录是禁止的.每次你必须使用普通的用户SSH远程登录以后,然后su切换到root这样,对于强迫症的我实在是很难容忍的 ...

  3. git 修改管理

    查看修改: 撤销某一文件的修改(还没提交): 撤销所有文件的修改: git checkout .

  4. java数学函数库 API(转)

    原文地址:http://www.24xuexi.com/w/2011-11-08/98206.html 首先给大家看看Math类所提供的主要方法,下面的列表给出了Math类的主要方法,如果要理解Mat ...

  5. [翻译]AKKA笔记 - LOGGING与测试ACTORS -2 (一)

    在前两章 ( 一 , 二 ) ,我们大致讲了Actor和message是怎么工作的,让我们看一下日志和测试我们的 TeacherActor . RECAP 这是上一节我们的Actor代码: class ...

  6. js 把url参数转对象

    //注意url中要含? function getParameterByName(name, url) {            if (!url) {                url = win ...

  7. EF架构~有时使用SQL更方便

    回到目录 在进行统计时,尤其是按月进行统计,由于我们采用的时间是一个2015-12-12日这种,所以在linq你无法进行拆分,你拆分了在发到SQL时也会报错,因为SQL那边更新不需要你.net的方法, ...

  8. Atitit  数据存储的分组聚合 groupby的实现attilax总结

    Atitit  数据存储的分组聚合 groupby的实现attilax总结 1. 聚合操作1 1.1. a.标量聚合 流聚合1 1.2. b.哈希聚合2 1.3. 所有的最优计划的选择都是基于现有统计 ...

  9. document对象

    document 对象是操作网页内容的 找元素 1.根据id找 document.getElementById(); 2.根据class找 document.getElementsByClassNam ...

  10. 利用Nodejs快速构建应用原型

    利用Nodejs快速构建应用原型 开发一个应用往往需要快速的构建原型,然后在此基础上设计和改进,前端可能立马能看到效果,但是后端业务逻辑不会那么快,这个时候其实我们需要额只是一些模拟数据,所以不需要真 ...