[经验分享]压力测试中因为Session而导致的OutOfMemoryException内存溢出问题及解决

游北亮
博客专家认证
2012-05-03 01:33:18
加精
这几天开发了一个Web站点,主要用于给其它站点做接口数据返回,在提交压力测试时,经常内存增长到1G后,溢出了,因为程序本身确实会加载不少缓存数据,而服务器本身是Windows2003 32位的操作系统,最大只支持2g内存,通过参考页面:http://support.microsoft.com/?kbID=810371,在boot.ini里增加启动参数:/3gb /Userva=3030
从而让程序可以使用到3G内存,然后继续提交压力测试

结果测试时,内存依旧持续增长,增长到2G后,又内存溢出了,至此,怀疑程序有内存泄露

找了一个win7版本的任务管理器,拷贝到服务器上,在内存达到2G时,创建了一个w3wp进程的dump(下面的创建转储文件)


抓下来dump文件后,在本地用Windbg加载sos.dll进行分析,通过!dumpheap -stat命令,发现占用内存最大的3个对象是:
6611b1b0 5027584 241324032 System.Web.SessionState.InProcSessionState
66149064 5027602 361987344 System.Web.Caching.CacheEntry
79330b24 5575562 378271928 System.String

各占用了241M、361M、378M,因为事先知道String占用这么大是正常的,所以要去分析另外2个对象,在Windbg里通过: !dumpheap -type System.Web.Caching.CacheEntry 命令,查看所有具体的CacheEntry地址等信息,这个会很多,数据一出来,马上按Ctrl+Break,数据大致如下
0:000> !dumpheap -type System.Web.Caching.CacheEntry
------------------------------
Heap 0
Address MT Size
106837a8 044b3ac4 72
107b0de0 044b3ac4 72
107b1e74 044b3ac4 72
107bdc14 044b3ac4 72
107bdef8 044b3ac4 72
107c1290 044b3ac4 72
107c1370 044b3ac4 72
107c142c 044b3ac4 72
107c20f8 044b3ac4 72

先找第一个地址分析:
0:000> !do 106837a8
Name: System.Web.Caching.CacheEntry
MethodTable: 044b3ac4
EEClass: 044aac24
Size: 72(0x48) bytes
(C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
MT Field Offset Type VT Attr Value Name
02469134 400140c 4 System.String 0 instance 10683740 _key
030fb350 400140d c System.Byte 1 instance 16 _bits
03154d18 400140e 8 System.Int32 1 instance -1350406618 _hashCode
02466d54 4001415 10 System.Object 0 instance 10683794 _value
0321ab48 4001416 1c System.DateTime 1 instance 106837c4 _utcCreated
0321ab48 4001417 24 System.DateTime 1 instance 106837cc _utcExpires
0321dda4 4001418 2c System.TimeSpan 1 instance 106837d4 _slidingExpiration
030fb350 4001419 d System.Byte 1 instance 4294967295 _expiresBucket
044b3964 400141a 34 ...g.ExpiresEntryRef 1 instance 106837dc _expiresEntryRef
030fb350 400141b e System.Byte 1 instance 2 _usageBucket
044b3a6c 400141c 38 ...ing.UsageEntryRef 1 instance 106837e0 _usageEntryRef
0321ab48 400141d 3c System.DateTime 1 instance 106837e4 _utcLastUpdate
044b32f0 400141e 14 ...g.CacheDependency 0 instance 00000000 _dependency
02466d54 400141f 18 System.Object 0 instance 00000000 _onRemovedTargets
0321ab48 4001412 1e4 System.DateTime 1 shared static NoAbsoluteExpiration
>> Domain:Value 000dd050:NotInit 0012ad78:1616ba94 <<
0321dda4 4001413 1e8 System.TimeSpan 1 shared static NoSlidingExpiration
>> Domain:Value 000dd050:NotInit 0012ad78:1616baa4 <<
0321dda4 4001414 1ec System.TimeSpan 1 shared static OneYear
>> Domain:Value 000dd050:NotInit 0012ad78:1616bab4 <<

然后再看_key的值
0:000> !do 10683740
Name: System.String
MethodTable: 02469134
EEClass: 02a42780
Size: 82(0x52) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: f2029612887/recommendservice.svc

再看_value的值
0:000> !do 10683794
Name: System.Web.Configuration.MapPathCacheInfo
MethodTable: 047026d0
EEClass: 04776bb8
Size: 20(0x14) bytes
(C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
Fields:
MT Field Offset Type VT Attr Value Name
02469134 40018d4 4 System.String 0 instance 10683dfc MapPathResult
0320bdb8 40018d5 c System.Boolean 1 instance 1 Evaluated
030f0498 40018d6 8 System.Exception 0 instance 00000000 CachedException
0:000> !do 10683dfc
Name: System.String
MethodTable: 02469134
EEClass: 02a42780
Size: 88(0x58) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: E:\wwwroot\Web\recommendservice.svc
看来第一个CacheEntry保存的是一个虚拟路径和物理路径的对应关系,接着分析下一个CacheEntry的数据
如此类推发现
!do 107b0de0得到的也是一个文件路径缓存数据
!do 107b1e74得到System.Web.Security.FileSecurityDescriptorWrapper,看起来像是文件安全配置说明
!do 107bdc14得到一个key为jbj1dejrlaibnmfyrnqldwk45的System.Web.SessionState.InProcSessionState对象
!do 107bdef8得到一个key为jfd2qmyinc5sgpk55b2nks4rw的System.Web.SessionState.InProcSessionState对象
107c1290、107c1370、107c142c、107c20f8等大部分CacheEntry保存的都是InProcSessionState对象

由此可见,占用内存第二大的,都引用了占用内存第三大的InProcSessionState对象,
而Asp.net的Session默认是保持20分钟,压力测试时,在20分钟内创建的Session全部被保持,无法被GC回收,从而内存不断增长,最终导致内存溢出了

马上咨询压力测试人员,果然是压力测试发起了所有连接,都是新连接,
通过修改压力测试方案,复用创建的连接,而不是发起 新连接,果然内存占用在400M左右,增长也极其缓慢

最后的修改方案,因为此站点仅作为接口使用,不会使用到Session,直接在Web.config增加配置:<sessionState mode="Off"></sessionState>
关闭Session,再进行压力测试,内存基本不会增长


原文链接
...全文
3443 86 打赏 收藏 转发到动态 举报
写回复
用AI写文章
86 条回复
切换为时间正序
请发表友善的回复…
发表回复
humin332 2013-05-07
  • 打赏
  • 举报
回复
迟来的 回复..谢谢分享
sglogin 2012-05-12
  • 打赏
  • 举报
回复
谢谢楼主分享
小九格物 2012-05-11
  • 打赏
  • 举报
回复
谢谢楼主分享。。。。 共同进步
aqeeaqee 2012-05-11
  • 打赏
  • 举报
回复
如果真的需要保存Session数据,也可以考虑Session持久化方案,而不是放在内存里。
wrost 2012-05-11
  • 打赏
  • 举报
回复
慢慢消化,先收藏起来
chuliushuang 2012-05-10
  • 打赏
  • 举报
回复
谢谢分享哈,学习了额
ln8023love 2012-05-10
  • 打赏
  • 举报
回复
这个调试好高端~也尝试下~
  • 打赏
  • 举报
回复
asp 的值得考虑
xboxeer 2012-05-08
  • 打赏
  • 举报
回复
这个调试好高端~也尝试下~
游北亮 2012-05-08
  • 打赏
  • 举报
回复
谢谢支持,请参考我前面的回复

[Quote=引用 63 楼 的回复:]
LZ的方法在解决内存溢出的时候确实可行,如果程序代码里面要用到session里的值呢。顶LZ,期待其他更好的解决方案。
[/Quote]
游北亮 2012-05-08
  • 打赏
  • 举报
回复
感谢你的回复
压力测试也是可以Post参数的,而且也会记录返回状态码的,如果返回状态码是200,就表示正常,返回其它状态码,就是失败

我们压力测试要求每次返回都必须是200,只是一开始都是使用全新连接提交压力测试,短时间内发起几百万次连接,根据我的内存分析结果,每个Session占用内存不高,但是量级上来了,就导致内存溢出了


[Quote=引用 57 楼 的回复:]
或许你的压力测试没有以业务逻辑测试为基础。你的压力测试(并且关闭session)的同时,可以保证每一次访问此网站其业务逻辑操作结果都正常吗?

如果原来的程序本身就需要session集合,那么你关闭session将是灾难性的。此时还是要求重写此程序,而不是简单地关闭session。假设一个程序偶尔才在session集合里边保存几十个字节,而不是滥用session集合,那么开启session也没……
[/Quote]
游北亮 2012-05-08
  • 打赏
  • 举报
回复
你说的对,因为在我的案例中,程序没有用到Session,所以就直接关闭了
如果要使用到Session,用你的方案也可以:压缩session过期时间,页面加心跳保持

或者参考2楼孟子的答复也可以


[Quote=引用 53 楼 的回复:]
引用 44 楼 的回复:
也不尽然,我这个接口的日访问量也达到至少3千万,这是正常访问量
每天最繁忙的时候的突发访问量还没有统计,你总不能把这个正常访问当成ddos吧,呵呵
当然要尽量高的负载



引用 36 楼 的回复:
在实际应用中,这个漏洞对应的只是个拒绝服务攻击吧,加个防火墙就行了么,用浏览器的那会产生那么多session啊。


没看后面人的回复,看来你跟本没用到……
[/Quote]
a2008zfh 2012-05-08
  • 打赏
  • 举报
回复
学习了,谢谢你的分享
charleswu82 2012-05-08
  • 打赏
  • 举报
回复
谢谢分享
Windows2003 32位的操作系统,支持的最大内存超过2G
zhangdaowu5 2012-05-08
  • 打赏
  • 举报
回复

LZ的方法在解决内存溢出的时候确实可行,如果程序代码里面要用到session里的值呢。顶LZ,期待其他更好的解决方案。
Payden 2012-05-08
  • 打赏
  • 举报
回复
支持一下!
  • 打赏
  • 举报
回复
或许你的压力测试没有以业务逻辑测试为基础。你的压力测试(并且关闭session)的同时,可以保证每一次访问此网站其业务逻辑操作结果都正常吗?

如果原来的程序本身就需要session集合,那么你关闭session将是灾难性的。此时还是要求重写此程序,而不是简单地关闭session。假设一个程序偶尔才在session集合里边保存几十个字节,而不是滥用session集合,那么开启session也没有什么问题。
Easy2HJ 2012-05-08
  • 打赏
  • 举报
回复
很多时候,这个方案不可行,不过可以解决你当前问题重要。
maybeyours 2012-05-08
  • 打赏
  • 举报
回复
嗷,原来。。。thanx,学习@
mbugaifc 2012-05-08
  • 打赏
  • 举报
回复
学习了,谢谢。
加载更多回复(39)

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

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