IOCP占用内存问题

xian_wwq 2013-05-10 05:42:15
问题描述:
使用IOCP做数据传输,压力测试时(200~300 client),软件功能正常,但内存占用会
持续上涨。当进程占用1.3G内存时,停止Server,可以看到线程数从70左右降为1,句柄数从2000左
右降为700,但内存仅释放400M左右,有900M未释放。
因为内存分配使用的是比较成熟的内存池,软件退出时根据日志可以判定没有内存泄
露。请大牛帮忙分析,这900M内存占用是什么原因引起的?是由于IOCP投递被系统锁定?
...全文
319 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
xian_wwq 2014-08-15
  • 打赏
  • 举报
回复
引用 11 楼 u014205366 的回复:
请问楼主可以提供UDMH的压缩包不,我在网上没有找到UDMH下载的。谢谢了!
没有注意到你的留言, umdh不是单独的工具,安装windbg工具后就有了
燕子-hh 2014-04-13
  • 打赏
  • 举报
回复
请问楼主可以提供UDMH的压缩包不,我在网上没有找到UDMH下载的。谢谢了!
燕子-hh 2014-04-13
  • 打赏
  • 举报
回复
楼主你好,我目前在进行IOCP的开发中也出现了这个问题。服务器连接60个客户端后(每个客户端每200ms发送一次数据给客户端),运行不了几个小时就出现问题了。其中cpu增长很快,如果连接1个客户端,其中也是以4K的速度增长,连接60个就差不多以60K的速度增长。 请帮忙指点下,我主要是引用了网上的IOCPS和Myiocp两个类。 其中类引用地址:http://www.pudn.com/downloads38/sourcecode/windows/network/detail126796.html#
xian_wwq 2013-05-21
  • 打赏
  • 举报
回复
引用 8 楼 ll1798cn 的回复:
UDMH 捕获分析下程序的堆内存的分配情况。
非常有效的工具,很快找到了业务层的BUG,目前看来和IOCP关联不大。 等压力测试后再结贴,谢谢大家的关注!
ll1798cn 2013-05-19
  • 打赏
  • 举报
回复
UDMH 捕获分析下程序的堆内存的分配情况。
xian_wwq 2013-05-17
  • 打赏
  • 举报
回复
引用 6 楼 luoshao20120430 的回复:
贴上你代码看看。
代码太长,我说说实现思路。IOCP以DLL的方式被上层业务调用。

typedef struct _SIOCPRWContext
{
	OVERLAPPED m_overlapped;	// 每一个重叠网络操作的重叠结构(针对每一个Socket的每一个操作,都要有一个)		20        
	EIOCPPostType m_ePostType;	// 标识网络操作的类型(对应上面的枚举)											4

	SOCKET	m_socket;			// 网络操作所使用的Socket													4
	WSABUF	m_wsaBuf;			// WSA类型的读缓冲区,用于给重叠操作传参数的				8
	DWORD	m_nTotalBytes;		// 数据总的字节数
	DWORD	m_nBytes;		    // 已读取的字节数,初始设置为0
	char*   m_pBufAddress;      // 缓冲区初始地址,在接收buffer调整时保存buffer首址 
	
}*PSIOCPRWContext,SIOCPRWContext;
const ULONG SIOCPRWContextSize = sizeof(SIOCPRWContext);

typedef struct _PER_SOCKET_Context
{  
	SOCKET      m_Socket;                               //连接客户端的socket
	SOCKADDR_IN m_ClientAddr;                           //客户端地址

} SIOCPSocketContext, *PIOCPSocketContext;
const ULONG SSIOCPSocketContextSize = sizeof(SIOCPSocketContext);

1. IOCP部分不理解业务,通过回调函数按照业务层的要求初始化Buffer. 写投递有一次memcpy动作;读投递兼顾拼包。 2. 上层业务要发送实时数据和历史数据(差别是实时数据业务层处理所需时间短,发送数据量较小,历史数据业务层处理所需时间较长,发送数据较大),目前测试发送实时数据,内存占用正常,进程退出可以完全释放;但发送历史数据时,内存占用会持续上涨,退出时有上述问题。 这种情况是否可以认定IOCP模块没有问题,问题是上层业务代码Bug导致的? 如有未说清楚的,请大牛们指点,我继续补充。
Luo_Bryant 2013-05-17
  • 打赏
  • 举报
回复
贴上你代码看看。
翅膀又硬了 2013-05-16
  • 打赏
  • 举报
回复
内存池的设计就是不释放,便于下次用。
yaozhiyong110 2013-05-16
  • 打赏
  • 举报
回复
自己代码问题 别怪到iocp头上...
LiuYinChina 2013-05-15
  • 打赏
  • 举报
回复
smwhotjay 2013-05-14
  • 打赏
  • 举报
回复
xian_wwq 2013-05-14
  • 打赏
  • 举报
回复
没人回?经过再次测试发现,内存占用似乎与服务器端投递的大数据包(1M左右)有关系。但是应该怎么解决呢?
说明 一、受限制库Dll和lib说明: 库文件G-TcpServer.lib、G-TcpServer.Dll是受限制的试用版本和Demo配套。 1、最大连接不能超过50 2、发送字节数不能超过50 3、不能设置0读投递、无超时机制 4、其他功能限制 二、版本解读说明 1、版本名带T的表示是受限制的试用版 2、版本名带bata表示非正式版 3、版本名带WChar表示支持WideChar 4、版本号以时间格式累加计数(60进1)和显示 三、文件“G-TcpServer无限制体验版.exe”是无限制的体验版。 四、文件夹 1、Demos文件夹是VC Lib、Dll库及Delphi2010的例程源码,供开发学习 2、G-Sockets文件夹是Lib和Dll库的Delphi和VC的头文件 3、Release\G-Sockets\Dll文件夹是动态库的输出文件夹 4、Release\G-Sockets\Lib文件夹是静态库的输出文件夹 1、模块包含处理线程、工作线程和看守线程。 工作线程负责IO投递工作,并响应投递返回,再把接收的数据投递给处理线程,处理线程调用回调函数给应用层,以此可以在通讯层和应用层之间现实0拷贝数据的功能。模块只有一个看守线程,负责:a、响应Accept事件并投递接受队列;b、效验接受超时(即只连接不发数据)断开连接,防止空连接;c、效验空闲超时(即心跳超时)断开连接。 按工作量来分,最繁重的是处理线程,其次是工作线程,最闲的是看守线程。可通过OnThread事件回调函数设置线程权限。在此线程模式下,应用层可以在回调函数里处理数据而不必再建立另外的数据处理线程池。 2、线程平衡 为使连接能平衡使用处理线程,每个连接同时只有一个处理线程处理工作线程投递过来的IO返回事件并调用回调函数通知应用层。 3、收发平衡 为使连接能平衡使用IO设备,每个连接同时只能投递一个读请求,并通过线程平衡机制保证接收的数据是按顺序的被处理线程处理及通过回调函数传递给应用层;同时也只能投递一个写请求,其余写请求都按顺序放在写队列里面。读写同步都使用临界段。 4、0拷贝技术 接收数据0拷贝看1项;提供GTcpSvr_AllocGBuf()、GTcpSvr_FreeGBuf()和GTcpSvr_PostSendGBuf()三个函数实现发送数据的0拷贝。 5、0读投递 为避免内核锁定分页内存过多,可通过设置来采用0读投递来降低吞吐性能从而实现大连接量。 6、HndData回收 HndData回收有多种方法,但额外会在收发数据这两个频率操作上增加工作量,因此尽量避免在这两个操作时做太多的工作是有必要的。模块均不采用“投递计数”或“投递链表”的方式来判断回收HndData的时机,而是一旦断线立即回收,其他未决投递继续返回时只处理IoData,不对HndData做任何写操作。同时为避免HndData刚收回来但其未决投递还没有完全返回之前就立即被利用的可能性,HndData池采用了FIFO双锁并发链表,该链表通过ExNumber值来实现在最大连接情况下HndData池还有ExNumber个数量使链表不为NULL,通过设置ExNumber数量可实现控制链表末端的HndData出列时间,在这个时间内可以保证断开刚回收的HndData的未决投递能够完全返回。 HndData池,初始时如下: HD1 + HD2 + HD... + HDMaxConnection + HDEx1 + HDEx2 + HDEx... + HDExNumber | | Head------------------------------------------------------------------Tail 达到最大连接时如下: HDEx1 + HDEx2 + HDEx... + HDExNumber | | Head---------------------------Tail 断开回收一个HndData(HD)后如下: HDEx1 + HDEx2 + HDEx... + HDExNumber + HD | | Head-------(需要T时间HD才能出列)------Tail 注:T可以通过控制ExNumber值来实现,假定每秒最大可以处理C个连接和断开,需要延时T秒所有未决投递才会完全回收,那么:ExNumber = T * (MaxConnection / C)。实际上每个连接未决投递非常少(因为读写是单投递的),并且工作线程并不处理数据工作量不大,因此T很短,模块默认是3秒。对于服务器而已,一秒能接受的连接量是可知预计的,模块默认是1万,假定MaxConnection=C所以ExNumber是3万。但实际应用中,正常情况下连接率远少于1万/S,尤其是长连接的服务器,即使是短连接的服务器也不会发生这样的连接率。可能的情况是DOS,如果是影响也不大,因为还有MaxConnection控制,超过这个数的连接就立即被CloseSocket了。和频率高的数据收发相比,断线、连接的频率远少于它,在频率低的地方上多做多点工作总比在频率高的地方多做一点工作的好。 7、可伸缩性 IoData数量可根据初始需要设置,资源不足时模块自动向系统申请内存。为保证HndData的安全性,HndData池还设置了延时出列,刚回收的HndData入列时间必须超过3(或更长)秒钟,如果未达到3秒的,模块自动向系统申请内存。 8、内存管理 IoData和HndData均采用VirtualAlloc和VirtualFree来向系统操作内存。IoData池采用原子函数InterlockedCompareExchange来操作进出栈。HndData采用单向FIFO双锁并发链表来管理出入列操作。其他小内存需求的均采用静态数组或new操作。 五、内存需求 每个IoData等于一个分页内存大小,信息头大小为36Byte,有效使用内存是4060Byte,因此对GTcpSvr_AllocGBuf获得的内存写入时不应该超过4060(调用GTcpSvr_GetGbufSize获得),所有IoData占用系统内存是:IoDataCount * PageSize(4096)。每个HndData大小是128Byte,加上每个Socket分配时耗内存约是:540Bytes(此为估计值,应以MS技术文档为准),所有HndData耗系统内存是:HndDataCount * MAX_HNDDATA_AND_SOCKET_SIZE(128 + 540)。其他变量和数组可能耗得内存在10M之下。 综上,整个模块需求内存量是:UseMemSize = IoDataCount * PageSize + HndDataCount * MAX_HNDDATA_AND_SOCKET_SIZE + 10M。
说明 一、本压缩包含: 1、Demo源码,位于:\Demos\G-TcpServerLibDemo\G-TcpServerLibDemo.vcproj 2、G-TcpServer模块头文件文件,位于:\G-Sockets\G-TcpServer.h(模块核心文件) 3、G-TcpServer模块Lib文件,位于:\G-Sockets\G-TcpServer.lib(模块核心文件) 4、无限制Demo exe文件:G-TcpServerLibDemo1.0.exe 二、受限制库G-TcpServer.lib说明: 库文件G-TcpServer.lib是受限制的试用版本和Demo配套。 1、最大连接不能超过100 2、发送字节数不能超过128 3、不能设置0读投递 4、其他功能限制 三、版本解读说明 1、版本名带T的表示是受限制的试用版,参阅二 2、版本名带bata表示非正式版 3、版本名带WChar表示支持WideChar 4、版本号以时间格式累加计数(60进1)和显示 四、技术说明 1、模块包含处理线程、工作线程和看守线程。 工作线程负责IO投递工作,并响应投递返回,再把接收的数据投递给处理线程,处理线程调用回调函数给应用层,以此可以在通讯层和应用层之间现实0拷贝数据的功能。模块只有一个看守线程,负责:a、响应Accept事件并投递接受队列;b、效验接受超时(即只连接不发数据)断开连接,防止空连接;c、效验空闲超时(即心跳超时)断开连接。 按工作量来分,最繁重的是处理线程,其次是工作线程,最闲的是看守线程。可通过OnThread事件回调函数设置线程权限。在此线程模式下,应用层可以在回调函数里处理数据而不必再建立另外的数据处理线程池。 2、线程平衡 为使连接能平衡使用处理线程,每个连接同时只有一个处理线程处理工作线程投递过来的IO返回事件并调用回调函数通知应用层。 3、收发平衡 为使连接能平衡使用IO设备,每个连接同时只能投递一个读请求,并通过线程平衡机制保证接收的数据是按顺序的被处理线程处理及通过回调函数传递给应用层;同时也只能投递一个写请求,其余写请求都按顺序放在写队列里面。读写同步都使用临界段。 4、0拷贝技术 接收数据0拷贝看1项;提供GTcpSvr_AllocGBuf()、GTcpSvr_FreeGBuf()和GTcpSvr_PostSendGBuf()三个函数实现发送数据的0拷贝。 5、0读投递 为避免内核锁定分页内存过多,可通过设置来采用0读投递来降低吞吐性能从而实现大连接量。 6、HndData回收 HndData回收有多种方法,但额外会在收发数据这两个频率操作上增加工作量,因此尽量避免在这两个操作时做太多的工作是有必要的。模块均不采用“投递计数”或“投递链表”的方式来判断回收HndData的时机,而是一旦断线立即回收,其他未决投递继续返回时只处理IoData,不对HndData做任何写操作。同时为避免HndData刚收回来但其未决投递还没有完全返回之前就立即被利用的可能性,HndData池采用了FIFO双锁并发链表,该链表通过ExNumber值来实现在最大连接情况下HndData池还有ExNumber个数量使链表不为NULL,通过设置ExNumber数量可实现控制链表末端的HndData出列时间,在这个时间内可以保证断开刚回收的HndData的未决投递能够完全返回。 HndData池,初始时如下: HD1 + HD2 + HD... + HDMaxConnection + HDEx1 + HDEx2 + HDEx... + HDExNumber | | Head------------------------------------------------------------------Tail 达到最大连接时如下: HDEx1 + HDEx2 + HDEx... + HDExNumber | | Head---------------------------Tail 断开回收一个HndData(HD)后如下: HDEx1 + HDEx2 + HDEx... + HDExNumber + HD | | Head-------(需要T时间HD才能出列)------Tail
服务器概要设计说明全文共5页,当前为第1页。服务器概要设计说明全文共5页,当前为第1页。 服务器概要设计说明全文共5页,当前为第1页。 服务器概要设计说明全文共5页,当前为第1页。 目录 功能概述 2 网络通信层 3 连接生命周期的管理 3 接口 3 异步IO缓冲内存池 3 本地数据与字节流数据的互相转换 4 信令和通信数据结构 5 伪代码定义 5 命令管理 7 数据有效性检测 8 文件传输通道 9 日志 10 功能概述 服务器主要业务功能是连接物管和终端,为社区物管和管理中心提供管理功能,使其能够统一管理终端。 服务器的功能模块包括: 数据管理,数据包括房屋数据、住户数据、配租数据、门禁卡数据、终端配置数据等; 状态管理,服务器需要维持物管和终端的连接,保持连接状态的可增删改查; 命令管理,物管和终端之间的交互命令有确认机制,命令通过服务器传递,服务器需要保证命令传递的可靠性; 数据有效性检测,服务器需要定期检测一些数据的有效性,包括配租数据是否〔临近〕到期、门禁卡白名单数据与终端定期交换等; 文件传输通道,包括软件版本升级、数据文件传输等; 日志。 网络通信层 通信层负责业务命令和数据的发送接收。由于物管、终端和服务器之间命令和数据需要精确送达,所有业务都采用TCP来实现。 IOCP模型是Windows服务器开发中性能最好的非阻塞异步IO模型,所以通信层采用IOCP模型构建。Windows下有五种非阻塞I/O模型:选择〔Select〕、异步选择〔WSAAsyncSelect〕、事件选择〔WSAEventSelect〕、重叠I/O〔Overlapped I/O〕和完成端服务器概要设计说明全文共5页,当前为第2页。服务器概要设计说明全文共5页,当前为第2页。口〔Completion Port>。Select是同步IO模型,同时处理的任务有限〔上限1024〕,不符合处理成千上万连接的要求;WSAAsyncSelect也是同步IO模型,以接收Windows消息为基础,不符合服务器控制台程序要求;WSAEventSelect也是同步IO模型,需要创建与连接数等同的事件内核对象,资源未能高效利用,也排除在外;上面三种IO模型其实是一回事,都是类select模型,适合开发小型服务器或者客户端程序,而不适合需要接受成千上万连接的服务器程序。Overlapped I/O是异步IO模型,但是它需要程序员关心线程池的实现和调度〔类似Linux下面的epoll模型,但是epoll是同步IO模型〕;而IOCP克服了上面四种模型的缺点,对实现XX接数的服务器有可靠的性能和较少的资源占用,而且伸缩性比较强,占用资源数跟连接数量相关,甚至可以用在客户端程序上面。 服务器概要设计说明全文共5页,当前为第2页。 服务器概要设计说明全文共5页,当前为第2页。 连接生命周期的管理 C++语言没有对象回收〔GC〕机制,生命周期的管理和防止内存泄露需要程序自己实现,而一条连接从产生后到销毁的过程中会有多个线程同时对其进行操作,同时读写甚至同时关闭,对象的多线程同步也需要程序实现。这里采用智能指针〔shared_ptr, stl_c++11〕来管理连接的生命周期,通信层维护各个连接在内存中唯一一份数据,同时提供引用计数,统计当前该数据被外界使用情况,当外界没有角色再需要该数据时〔引用计数减到0〕,通信层会删除这份数据,同时表明该连接生命周期终止。 接口 数据接口采用handle/body手法,连接的handle采用整形数据,body采用C++对象封装连接数据,数据包含SOCKET句柄、连接状态和当前接收缓存〔业务层〕等。连接生命周期反映到handle上表现为该handle是否为有效。发送内存采用智能指针〔unique_ptr, stl_c++11〕进行传递,这里用到了智能指针对数据和数据析构的封装,发送完成之后直接调用其删除器〔deleter〕进行内存的删除,这样上下层之间就避免了一次内存拷贝。 回调接口为C++接口〔纯虚函数〕。 异步IO缓冲内存池 由于系统层和stl层容器都实现了小内存内存池,所以程序将不再实现自己的内存池,发送缓冲内存完全动态分配,接收缓冲内存每个连接有一份,也通过动态分配而来。 本地数据与字节流数据的互相转换 本地数据转 为字节流数据时,根据本地数据大小构造字节流对象,然后将本地数据逐字节填入流中,可变数组先填入数组大小再逐个填充数组内容。 字节流数据转换为本地数据时,根据字节流中标识的大小动态构造本地数据,构造时使用智能指针〔unique_ptr, stl_c++11〕管理数据,加上C++多态特性,可以大大简化内存的管理。 服务器概要设计说明全文共5页,当前为第3页。服务器概要设计说明全文共5页,当前为第3页。信令和通信数据结构 服务器概要

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧