谈谈最近优化一个网站项目的经验,首先说一下背景情况:

1) 在页面后台代码中我们把页面上大部分的HTML都使用字符串来拼接生成然后直接赋值给LiteralControl。

2) 网站CPU很高,基本都在80%左右,即使使用了StringBuilder来拼接字符串性能也不理想。

3) 为了改善性能,把整个字符串保存在memcached中,性能还是不理想。

在比较了这个网站和其它网站服务器上相关性能监视器指标后发现有一个参数特别显眼:

就是其中的每秒分配字节数,这个性能比较差的网站每秒分配2GB的内存(而且需要注意由于性能监视器是每秒更新一下,对于一个非常健康的网站这个值应该经常看到是0才对)!而其它一些网站只分配200M左右的内存。服务器配备4G内存,而每秒分配2G内存,我想垃圾回收器一定需要不断运行来回收这些内存。观察%Time in GC可以发现,这个值一直在10%左右,也就是说上次回收到这次回收间隔10秒的话,这次垃圾回收1秒,由于回收的时间相对固定,那么这个值可以反映回收的频繁度。

知道了这个要点就知道了方向,在项目中找可能的问题点:

1) 是否分配了大量临时的小对象

2) 是否分配了数量不多但比较大的大对象

在经历了一番查找之后,发现一个比较大的问题,虽然使用了memcached来缓存整个页面的HTML,但是在输出之前居然进行了几次string的Replace操作,这样就产生了几个大的字符串,我们来做一个实验模拟这种场景:

public partial class _Default : System.Web.UI.Page
{static string template;protected void Page_Load(object sender, EventArgs e){if (template == null){StringBuilder sb = new StringBuilder();for (int i = 0; i < 10000; i++)sb.Append("1234567890");template = sb.ToString(); }Stopwatch sw = Stopwatch.StartNew();for (int i = 0; i < 1; i++){long mem1 = GC.GetTotalMemory(false);string s = template + i;long mem2 = GC.GetTotalMemory(false);Response.Write((mem2 - mem1).ToString("N0"));Response.Write("<br/>");GC.KeepAlive(s);}for (int i = 0; i < 100000; i++){double d = Math.Sqrt(i);}Thread.Sleep(30);Response.Write(sw.ElapsedMilliseconds);}
}

在这段代码中:

1) 我们首先使用一个静态变量模拟缓存中的待输出的HTML

2) 我们中间的一段代码测算一下这个字符串占用的内存空间

3) 随后我们做了一些消耗CPU的运算操作来模拟页面的一些计算

4) 然后休眠一段时间

4) 最后我们输出了页面执行时间

我们这么做的目的是模拟一个比较“正常的”ASP.NET页面需要做的一些工作:

1) 内存上的分配

2) 一些计算

3) 涉及到IO访问的一些等待

来看看输出结果:

这里可以看到,我们这个字符串占用差不多200K的字节,字符串是字符数组,CLR中字符采用Unicode双字节存储,因此10万长度的字符串占用200千字节,并且也可以看到这个页面执行时间30毫秒,差不多是一个正常aspx页面的时间,而200K不到的字符串也差不多相当于这个页面的HTML片段,现在我们来改一下其中的一段代码模拟优化前进行的Replace操作带来的几个大字符串:

for (int i = 0; i < 10; i++)
{//long mem1 = GC.GetTotalMemory(false);string s = template + i;//long mem2 = GC.GetTotalMemory(false);//Response.Write((mem2 - mem1).ToString("N0"));//Response.Write("<br/>");//GC.KeepAlive(s);
}

然后使用IDE自带压力测试1000常量用户来测试这个页面:

可以看到每秒分配了超过400M字节(这和我们线上环境比还差点毕竟请求少),CPU占用基本在120-160左右(双核),我们去掉每秒分配内存这个数值,来看看垃圾回收频率和CPU占用两个值的图表:

可以看到红色的CPU波动基本和蓝色的垃圾回收波动保持一致(这里不太准确的另外一个原因是压力测试客户端运行于本机,而为w3wp关联2个处理器)!为什么说垃圾回收会带来CPU的波动,从理论上来说有以下原因:

1) 垃圾回收的时候会暂时挂起所有线程,然后GC会检测扫描每一个线程栈上可回收对象,然后会移动对象,并且重新设置对象指针,这整个过程首先是消耗CPU的

2) 而且在这个过程之后恢复线程执行,这个时候CPU往往会引起一个高峰因为已经有更多的请求等待了

我们把Math.Sqrt这段代码注释掉并且把w3wp和VSTestHost关联到不同的处理器来看看对于CPU计算很少的页面,上图更明显的对比:

这说明垃圾回收的确会占用很多CPU资源,但这只是一部分,其实我觉得网站的CPU压力来自于几个地方:

1) 就是大量的内存分配带来的垃圾回收所占用的CPU,对于ASP.NET框架内部的很多行为无法控制,但是可以在代码中尽量避免在堆上产生很多不必要的对象

2) 是实际的CPU运算,不涉及IO的运算,这些可以通过改良算法来优化,但是优化比较有限

3) 是IO操作这块,数据量的多少很关键,还有要考虑memcached等外部缓存对象序列化反序列化的消耗

4) 虽然很多IO操作不占用CPU资源,线程处于休眠状态,但是很多时候其实是依托新线程进行的,带来的就是线程切换和线程创建消耗的消耗,这一块可以通过合理使用多线程来优化

发现了这个问题之后优化就很简单了,把Replace操作放到memcached的Set操作之前,取出之后不产生过多大字符串,把for循环改为一次,再来看一下:

这次内存分配明显少了很多,CPU降下来了,降的不多,但从压力测试监视器中看到页面执行平均时间从5秒变为3秒了,每秒平均请求数从170到了200(最高从200到了300)。在这里要说明一点很多时候网站的性能优化不能光看CPU还要对比优化前后网站的负载,因为在优化之后页面执行时间降低了,负载量就增大了CPU消耗也随之增大。并且可以看到垃圾回收频率的缩短很明显,从长期在30%到几十秒一次30%。

最后想补充几点:

1) 有的时候我们会使用GC.GetTotalMemory(true); 来得到垃圾回收之后内存分配数,类似这样涉及到垃圾回收的代码在项目上线后千万不能出现,否则很可能会% Time in GC达到80%以上大量占用CPU。

2) 对于放在缓存中的对象我们往往会觉得性能得到保障大量去使用,其实缓存实现的只是把创造这个对象过程的时间转化为空间,而在拿到这个对象之后再进行很多运算带来的大量空间始终会进行垃圾回收。做网站和做应用程序不一样,一个操作如果申请200K堆内存,一个页面执行这个操作10次,一秒200多个请求,大家可以自己算一下平均每秒需要分配多少内存,这个数值是相当可怕的,网站是一个多线程的环境,我们对内存的使用要考虑更多。

项目优化经验mdash;mdash;垃圾回收导致的性能问题[z]相关推荐

  1. 项目优化经验——垃圾回收导致的性能问题

    谈谈最近优化一个网站项目的经验,首先说一下背景情况: 1) 在页面后台代码中我们把页面上大部分的HTML都使用字符串来拼接生成然后直接赋值给LiteralControl. 2) 网站CPU很高,基本都 ...

  2. 计算机毕业设计-ssm垃圾分类管理系统(项目+文档)小区垃圾回收分类处理系统

    计算机毕业设计-ssm垃圾分类管理系统(项目+文档)小区垃圾回收分类处理系统 注意:该项目只展示部分功能,如需了解,评论区咨询即可. 作者:IT跃迁谷 1.开发环境 开发语言:Java 后台框架:SS ...

  3. 【Android 内存优化】内存抖动 ( 垃圾回收算法总结 | 分代收集算法补充 | 内存抖动排查 | 内存抖动操作 | 集合选择 )

    文章目录 一. 垃圾回收算法总结 二. 分代收集算法补充 三. 查看 Java 虚拟机 四. 获取 Android 应用可使用最大内存 五. 内存抖动标志 六. 排查内存抖动 七. 常见的造成内存抖动 ...

  4. java垃圾回收system_java应用性能调优之详解System的gc垃圾回收方法

    1.什么是System.gc()? System.gc()是用Java,C#和许多其余流行的高级编程语言提供的API.当它被调用时,它将尽最大努力从内存中清除垃圾(即未被引用的对象).名词解释:GC, ...

  5. UE4 项目优化经验(性能、资源管理、打包等优化)

    UE4 优化(性能.资源管理.打包等优化) 一.如何查看性能指标 二.性能优化的方式 优化帧率 优化UE4项目启动速度 优化打包速度和缩小包容量 采用Jenkins进行持续集成交付 删除不用资源 三. ...

  6. C#垃圾回收学习总结

    浅谈C#垃圾回收 http://www.cnblogs.com/cuiyiming/archive/2013/03/26/2981931.html 理解C#垃圾回收机制我们首先说一下CLR(公共语言运 ...

  7. golang 所有进程休眠_golang 垃圾回收(三)插入写屏障

    并发的垃圾回收 STW 安全的回收 并发的垃圾回收 插入写屏障 伪代码 对象丢失的必要条件 写屏障是怎么解决问题? 并发的垃圾回收 golang 语言设计的根本性追求就是高并发,低延迟,所以golan ...

  8. 垃圾回收机制GC知识再总结兼谈如何用好GC(转)

    作者:Jeff Wong  出处:http://jeffwongishandsome.cnblogs.com/  本文版权归作者和博客园共有,欢迎围观转载.转载时请您务必在文章明显位置给出原文链接,谢 ...

  9. 理解 Java 垃圾回收机制

    理解java垃圾回收机制有什么好处呢?作为一个软件工程师,满足自己的好奇心将是一个很好的理由,不过更重要的是,理解GC工作机制可以帮助你写出更好的Java应用程序. 这是我个人的主观观点,但我相信一个 ...

最新文章

  1. Javascript 装饰器极速指南
  2. 关于USART接收中断的BUG和注意事项
  3. python 操作ipynb文件笔记
  4. excel几个表合成一张_快速将多个excel表合并成一个excel表
  5. oracle10数据库链接失败,PLSQL Developer连接Oracle 10g或Oracle 11g失败
  6. html5 websocket 手机,HTML5 WebSocket 示范
  7. 编写安全代码:小心使用浮点数
  8. serializable接口_面试官:RandomAccess这个空接口有何用?
  9. C# .NET 爬虫抓取京东商城所有商品分类
  10. Flash的荣辱兴衰史【转载】
  11. M1芯片的Mac在开发iOS项目时遇到的问题汇总(模拟器无法运行,Cocoapods错误等)
  12. Android 防止App退出 或者 启动另一个App
  13. iP138版 离线iP数据库ip.txt导入Mysql示例
  14. 类文件解析004-解析常量池元素
  15. 【微分方程数值解】有限差分法(二)两点边值问题数值算例(附python代码)
  16. 后台界面设计之表格设计
  17. 区块链国内外产业发展现状
  18. Random()类生成随机数详解
  19. 【数据架构】SOGAF 通用实体框架 (CoE)
  20. 做设计用什么笔记本电脑好?

热门文章

  1. 信息学奥赛一本通(1207:求最大公约数问题)
  2. 信息学奥赛一本通(1121:计算矩阵边缘元素之和)
  3. 数论 —— 整数分解
  4. 完全背包问题(信息学奥数一本通-T1268)
  5. Exploration(POJ-3618)
  6. 6 QM配置-质量计划配置-定义检验特性的编号范围
  7. 6 如何查看MESSAGE消息
  8. java ldap 父_java – DirContext:Active Directory Ldap请求:获取具有父组的用户组
  9. OpenCV Mat主要用法(2)_MatExpr
  10. STM32那点事(5)_ADC(下)